diff options
Diffstat (limited to 'libgo')
596 files changed, 31983 insertions, 7420 deletions
diff --git a/libgo/MERGE b/libgo/MERGE index 5b7344c0..2858637 100644 --- a/libgo/MERGE +++ b/libgo/MERGE @@ -1,4 +1,4 @@ -a7bd9a33067b +7ebbddd21330 The first line of this file holds the Mercurial revision number of the last merge done from the master library sources. diff --git a/libgo/Makefile.am b/libgo/Makefile.am index 957f23c..9a8a1b4 100644 --- a/libgo/Makefile.am +++ b/libgo/Makefile.am @@ -37,7 +37,8 @@ AM_CPPFLAGS = -I $(srcdir)/runtime $(LIBFFIINCS) $(PTHREAD_CFLAGS) ACLOCAL_AMFLAGS = -I ./config -I ../config -AM_CFLAGS = -fexceptions -fplan9-extensions $(SPLIT_STACK) $(WARN_CFLAGS) \ +AM_CFLAGS = -fexceptions -fnon-call-exceptions -fplan9-extensions \ + $(SPLIT_STACK) $(WARN_CFLAGS) \ $(STRINGOPS_FLAG) $(OSCFLAGS) \ -I $(srcdir)/../libgcc -I $(srcdir)/../libbacktrace \ -I $(MULTIBUILDTOP)../../gcc/include @@ -103,6 +104,7 @@ toolexeclibgo_DATA = \ bufio.gox \ bytes.gox \ crypto.gox \ + encoding.gox \ errors.gox \ expvar.gox \ flag.gox \ @@ -251,6 +253,11 @@ toolexeclibgoimage_DATA = \ image/jpeg.gox \ image/png.gox +toolexeclibgoimagecolordir = $(toolexeclibgoimagedir)/color + +toolexeclibgoimagecolor_DATA = \ + image/color/palette.gox + toolexeclibgoindexdir = $(toolexeclibgodir)/index toolexeclibgoindex_DATA = \ @@ -573,6 +580,9 @@ go_bytes_c_files = \ go_crypto_files = \ go/crypto/crypto.go +go_encoding_files = \ + go/encoding/encoding.go + go_errors_files = \ go/errors/errors.go @@ -669,7 +679,7 @@ go_net_fd_os_file = go_net_newpollserver_file = else # !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS if LIBGO_IS_NETBSD -go_net_fd_os_file = go/net/fd_bsd.go +go_net_fd_os_file = go_net_newpollserver_file = else # !LIBGO_IS_NETBSD && !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS # By default use select with pipes. Most systems should have @@ -726,9 +736,13 @@ else if LIBGO_IS_FREEBSD go_net_sendfile_file = go/net/sendfile_freebsd.go else +if LIBGO_IS_DRAGONFLY +go_net_sendfile_file = go/net/sendfile_dragonfly.go +else go_net_sendfile_file = go/net/sendfile_stub.go endif endif +endif if LIBGO_IS_LINUX go_net_interface_file = go/net/interface_linux.go @@ -736,9 +750,13 @@ else if LIBGO_IS_NETBSD go_net_interface_file = go/net/interface_netbsd.go else +if LIBGO_IS_DRAGONFLY +go_net_interface_file = go/net/interface_dragonfly.go +else go_net_interface_file = go/net/interface_stub.go endif endif +endif if LIBGO_IS_LINUX go_net_cloexec_file = go/net/sock_cloexec.go @@ -746,13 +764,13 @@ else go_net_cloexec_file = go/net/sys_cloexec.go endif -if LIBGO_IS_LINUX -go_net_poll_file = go/net/fd_poll_runtime.go +if LIBGO_IS_OPENBSD +go_net_tcpsockopt_file = go/net/tcpsockopt_openbsd.go else if LIBGO_IS_DARWIN -go_net_poll_file = go/net/fd_poll_runtime.go +go_net_tcpsockopt_file = go/net/tcpsockopt_darwin.go else -go_net_poll_file = go/net/fd_poll_unix.go +go_net_tcpsockopt_file = go/net/tcpsockopt_unix.go endif endif @@ -766,6 +784,7 @@ go_net_files = \ go/net/dnsconfig_unix.go \ go/net/dnsmsg.go \ $(go_net_newpollserver_file) \ + go/net/fd_mutex.go \ go/net/fd_unix.go \ $(go_net_fd_os_file) \ go/net/file_unix.go \ @@ -783,18 +802,21 @@ go_net_files = \ go/net/net.go \ go/net/parse.go \ go/net/pipe.go \ - $(go_net_poll_file) \ + go/net/fd_poll_runtime.go \ go/net/port.go \ go/net/port_unix.go \ + go/net/race0.go \ $(go_net_sendfile_file) \ + go/net/singleflight.go \ go/net/sock_posix.go \ - go/net/sock_unix.go \ $(go_net_sock_file) \ go/net/sockopt_posix.go \ $(go_net_sockopt_file) \ $(go_net_sockoptip_file) \ go/net/tcpsock.go \ go/net/tcpsock_posix.go \ + go/net/tcpsockopt_posix.go \ + $(go_net_tcpsockopt_file) \ go/net/udpsock.go \ go/net/udpsock_posix.go \ go/net/unixsock.go \ @@ -818,6 +840,12 @@ go_os_dir_file = go/os/dir_regfile.go endif endif +if LIBGO_IS_DARWIN +go_os_getwd_file = go/os/getwd_darwin.go +else +go_os_getwd_file = +endif + if LIBGO_IS_LINUX go_os_sys_file = go/os/sys_linux.go else @@ -854,6 +882,9 @@ else if LIBGO_IS_NETBSD go_os_stat_file = go/os/stat_atimespec.go else +if LIBGO_IS_DRAGONFLY +go_os_stat_file = go/os/stat_dragonfly.go +else go_os_stat_file = go/os/stat.go endif endif @@ -861,6 +892,7 @@ endif endif endif endif +endif if LIBGO_IS_LINUX go_os_pipe_file = go/os/pipe_linux.go @@ -874,7 +906,7 @@ go_os_files = \ go/os/doc.go \ go/os/env.go \ go/os/error.go \ - go/os/error_posix.go \ + go/os/error_unix.go \ go/os/exec.go \ go/os/exec_posix.go \ go/os/exec_unix.go \ @@ -882,6 +914,7 @@ go_os_files = \ go/os/file_posix.go \ go/os/file_unix.go \ go/os/getwd.go \ + $(go_os_getwd_file) \ go/os/path.go \ go/os/path_unix.go \ $(go_os_pipe_file) \ @@ -970,7 +1003,10 @@ go_strings_files = \ go/strings/reader.go \ go/strings/replace.go \ go/strings/search.go \ - go/strings/strings.go + go/strings/strings.go \ + go/strings/strings_decl.go +go_strings_c_files = \ + go/strings/indexbyte.c go_sync_files = \ go/sync/cond.go \ @@ -1000,6 +1036,7 @@ go_syslog_c_files = \ go_testing_files = \ go/testing/allocs.go \ go/testing/benchmark.go \ + go/testing/cover.go \ go/testing/example.go \ go/testing/testing.go @@ -1048,6 +1085,7 @@ go_archive_tar_files = \ go_archive_zip_files = \ go/archive/zip/reader.go \ + go/archive/zip/register.go \ go/archive/zip/struct.go \ go/archive/zip/writer.go @@ -1098,6 +1136,7 @@ go_crypto_cipher_files = \ go/crypto/cipher/cfb.go \ go/crypto/cipher/cipher.go \ go/crypto/cipher/ctr.go \ + go/crypto/cipher/gcm.go \ go/crypto/cipher/io.go \ go/crypto/cipher/ofb.go go_crypto_des_files = \ @@ -1110,7 +1149,8 @@ go_crypto_ecdsa_files = \ go/crypto/ecdsa/ecdsa.go go_crypto_elliptic_files = \ go/crypto/elliptic/elliptic.go \ - go/crypto/elliptic/p224.go + go/crypto/elliptic/p224.go \ + go/crypto/elliptic/p256.go go_crypto_hmac_files = \ go/crypto/hmac/hmac.go go_crypto_md5_files = \ @@ -1125,6 +1165,7 @@ go_crypto_rc4_files = \ go/crypto/rc4/rc4_ref.go go_crypto_rsa_files = \ go/crypto/rsa/pkcs1v15.go \ + go/crypto/rsa/pss.go \ go/crypto/rsa/rsa.go go_crypto_sha1_files = \ go/crypto/sha1/sha1.go \ @@ -1308,11 +1349,15 @@ go_image_color_files = \ go/image/color/color.go \ go/image/color/ycbcr.go +go_image_color_palette_files = \ + go/image/color/palette/palette.go + go_image_draw_files = \ go/image/draw/draw.go go_image_gif_files = \ - go/image/gif/reader.go + go/image/gif/reader.go \ + go/image/gif/writer.go go_image_jpeg_files = \ go/image/jpeg/fdct.go \ @@ -1766,6 +1811,7 @@ libgo_go_objs = \ bytes.lo \ bytes/index.lo \ crypto.lo \ + encoding.lo \ errors.lo \ expvar.lo \ flag.lo \ @@ -1787,6 +1833,7 @@ libgo_go_objs = \ sort.lo \ strconv.lo \ strings.lo \ + strings/index.lo \ sync.lo \ syscall.lo \ syscall/errno.lo \ @@ -1863,6 +1910,7 @@ libgo_go_objs = \ net/http/httputil.lo \ net/http/pprof.lo \ image/color.lo \ + image/color/palette.lo \ image/draw.lo \ image/gif.lo \ image/jpeg.lo \ @@ -2033,6 +2081,15 @@ crypto/check: $(CHECK_DEPS) @$(CHECK) .PHONY: crypto/check +@go_include@ encoding.lo.dep +encoding.lo.dep: $(go_encoding_files) + $(BUILDDEPS) +encoding.lo: $(go_encoding_files) + $(BUILDPACKAGE) +encoding/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: encoding/check + @go_include@ errors.lo.dep errors.lo.dep: $(go_errors_files) $(BUILDDEPS) @@ -2214,6 +2271,9 @@ strings.lo.dep: $(go_strings_files) $(BUILDDEPS) strings.lo: $(go_strings_files) $(BUILDPACKAGE) +strings/index.lo: $(go_strings_c_files) + @$(MKDIR_P) strings + $(LTCOMPILE) -c -o strings/index.lo $(srcdir)/go/strings/indexbyte.c strings/check: $(CHECK_DEPS) @$(CHECK) .PHONY: strings/check @@ -2821,6 +2881,15 @@ image/color/check: $(CHECK_DEPS) @$(CHECK) .PHONY: image/color/check +@go_include@ image/color/palette.lo.dep +image/color/palette.lo.dep: $(go_image_color_palette_files) + $(BUILDDEPS) +image/color/palette.lo: $(go_image_color_palette_files) + $(BUILDPACKAGE) +image/color/palette/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: image/color/palette/check + @go_include@ image/draw.lo.dep image/draw.lo.dep: $(go_image_draw_files) $(BUILDDEPS) @@ -3236,6 +3305,8 @@ bytes.gox: bytes.lo $(BUILDGOX) crypto.gox: crypto.lo $(BUILDGOX) +encoding.gox: encoding.lo + $(BUILDGOX) errors.gox: errors.lo $(BUILDGOX) expvar.gox: expvar.lo @@ -3433,6 +3504,9 @@ image/jpeg.gox: image/jpeg.lo image/png.gox: image/png.lo $(BUILDGOX) +image/color/palette.gox: image/color/palette.lo + $(BUILDGOX) + index/suffixarray.gox: index/suffixarray.lo $(BUILDGOX) diff --git a/libgo/Makefile.in b/libgo/Makefile.in index 706a72e..63e78b4 100644 --- a/libgo/Makefile.in +++ b/libgo/Makefile.in @@ -105,6 +105,7 @@ am__installdirs = "$(DESTDIR)$(toolexeclibdir)" \ "$(DESTDIR)$(toolexeclibgohashdir)" \ "$(DESTDIR)$(toolexeclibgohtmldir)" \ "$(DESTDIR)$(toolexeclibgoimagedir)" \ + "$(DESTDIR)$(toolexeclibgoimagecolordir)" \ "$(DESTDIR)$(toolexeclibgoindexdir)" \ "$(DESTDIR)$(toolexeclibgoiodir)" \ "$(DESTDIR)$(toolexeclibgologdir)" \ @@ -132,12 +133,12 @@ libgobegin_a_OBJECTS = $(am_libgobegin_a_OBJECTS) LTLIBRARIES = $(toolexeclib_LTLIBRARIES) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \ - errors.lo expvar.lo flag.lo fmt.lo hash.lo html.lo image.lo \ - io.lo log.lo math.lo mime.lo net.lo os.lo path.lo \ + encoding.lo errors.lo expvar.lo flag.lo fmt.lo hash.lo html.lo \ + image.lo io.lo log.lo math.lo mime.lo net.lo os.lo path.lo \ reflect-go.lo reflect/makefunc.lo regexp.lo runtime-go.lo \ - sort.lo strconv.lo strings.lo sync.lo syscall.lo \ - syscall/errno.lo syscall/signame.lo syscall/wait.lo testing.lo \ - time-go.lo unicode.lo archive/tar.lo archive/zip.lo \ + sort.lo strconv.lo strings.lo strings/index.lo sync.lo \ + syscall.lo syscall/errno.lo syscall/signame.lo syscall/wait.lo \ + testing.lo time-go.lo unicode.lo archive/tar.lo archive/zip.lo \ compress/bzip2.lo compress/flate.lo compress/gzip.lo \ compress/lzw.lo compress/zlib.lo container/heap.lo \ container/list.lo container/ring.lo crypto/aes.lo \ @@ -157,13 +158,13 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.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/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 \ + 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 \ sync/atomic.lo sync/atomic_c.lo text/scanner.lo \ text/tabwriter.lo text/template.lo text/template/parse.lo \ @@ -260,16 +261,16 @@ DATA = $(toolexeclibgo_DATA) $(toolexeclibgoarchive_DATA) \ $(toolexeclibgodebug_DATA) $(toolexeclibgoencoding_DATA) \ $(toolexeclibgoexp_DATA) $(toolexeclibgogo_DATA) \ $(toolexeclibgohash_DATA) $(toolexeclibgohtml_DATA) \ - $(toolexeclibgoimage_DATA) $(toolexeclibgoindex_DATA) \ - $(toolexeclibgoio_DATA) $(toolexeclibgolog_DATA) \ - $(toolexeclibgomath_DATA) $(toolexeclibgomime_DATA) \ - $(toolexeclibgonet_DATA) $(toolexeclibgonethttp_DATA) \ - $(toolexeclibgonetrpc_DATA) $(toolexeclibgoold_DATA) \ - $(toolexeclibgoos_DATA) $(toolexeclibgopath_DATA) \ - $(toolexeclibgoregexp_DATA) $(toolexeclibgoruntime_DATA) \ - $(toolexeclibgosync_DATA) $(toolexeclibgotesting_DATA) \ - $(toolexeclibgotext_DATA) $(toolexeclibgotexttemplate_DATA) \ - $(toolexeclibgounicode_DATA) + $(toolexeclibgoimage_DATA) $(toolexeclibgoimagecolor_DATA) \ + $(toolexeclibgoindex_DATA) $(toolexeclibgoio_DATA) \ + $(toolexeclibgolog_DATA) $(toolexeclibgomath_DATA) \ + $(toolexeclibgomime_DATA) $(toolexeclibgonet_DATA) \ + $(toolexeclibgonethttp_DATA) $(toolexeclibgonetrpc_DATA) \ + $(toolexeclibgoold_DATA) $(toolexeclibgoos_DATA) \ + $(toolexeclibgopath_DATA) $(toolexeclibgoregexp_DATA) \ + $(toolexeclibgoruntime_DATA) $(toolexeclibgosync_DATA) \ + $(toolexeclibgotesting_DATA) $(toolexeclibgotext_DATA) \ + $(toolexeclibgotexttemplate_DATA) $(toolexeclibgounicode_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ @@ -443,7 +444,8 @@ WARN_CFLAGS = $(WARN_FLAGS) $(WERROR) # -I/-D flags to pass when compiling. AM_CPPFLAGS = -I $(srcdir)/runtime $(LIBFFIINCS) $(PTHREAD_CFLAGS) ACLOCAL_AMFLAGS = -I ./config -I ../config -AM_CFLAGS = -fexceptions -fplan9-extensions $(SPLIT_STACK) $(WARN_CFLAGS) \ +AM_CFLAGS = -fexceptions -fnon-call-exceptions -fplan9-extensions \ + $(SPLIT_STACK) $(WARN_CFLAGS) \ $(STRINGOPS_FLAG) $(OSCFLAGS) \ -I $(srcdir)/../libgcc -I $(srcdir)/../libbacktrace \ -I $(MULTIBUILDTOP)../../gcc/include @@ -506,6 +508,7 @@ toolexeclibgo_DATA = \ bufio.gox \ bytes.gox \ crypto.gox \ + encoding.gox \ errors.gox \ expvar.gox \ flag.gox \ @@ -640,6 +643,10 @@ toolexeclibgoimage_DATA = \ image/jpeg.gox \ image/png.gox +toolexeclibgoimagecolordir = $(toolexeclibgoimagedir)/color +toolexeclibgoimagecolor_DATA = \ + image/color/palette.gox + toolexeclibgoindexdir = $(toolexeclibgodir)/index toolexeclibgoindex_DATA = \ index/suffixarray.gox @@ -865,6 +872,9 @@ go_bytes_c_files = \ go_crypto_files = \ go/crypto/crypto.go +go_encoding_files = \ + go/encoding/encoding.go + go_errors_files = \ go/errors/errors.go @@ -955,7 +965,7 @@ go_mime_files = \ # By default use select with pipes. Most systems should have # something better. @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_select.go -@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_bsd.go +@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = @LIBGO_IS_RTEMS_TRUE@go_net_fd_os_file = go/net/fd_select.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = @@ -986,17 +996,19 @@ go_mime_files = \ @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_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_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go +@LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go +@LIBGO_IS_DRAGONFLY_TRUE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_dragonfly.go @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_freebsd.go @LIBGO_IS_LINUX_TRUE@go_net_sendfile_file = go/net/sendfile_linux.go -@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_stub.go +@LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_stub.go +@LIBGO_IS_DRAGONFLY_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_dragonfly.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@go_net_interface_file = go/net/interface_netbsd.go @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_LINUX_FALSE@go_net_poll_file = go/net/fd_poll_unix.go -@LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_poll_file = go/net/fd_poll_runtime.go -@LIBGO_IS_LINUX_TRUE@go_net_poll_file = go/net/fd_poll_runtime.go +@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_OPENBSD_FALSE@go_net_tcpsockopt_file = go/net/tcpsockopt_unix.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 = \ go/net/cgo_unix.go \ $(go_net_cgo_file) \ @@ -1007,6 +1019,7 @@ go_net_files = \ go/net/dnsconfig_unix.go \ go/net/dnsmsg.go \ $(go_net_newpollserver_file) \ + go/net/fd_mutex.go \ go/net/fd_unix.go \ $(go_net_fd_os_file) \ go/net/file_unix.go \ @@ -1024,18 +1037,21 @@ go_net_files = \ go/net/net.go \ go/net/parse.go \ go/net/pipe.go \ - $(go_net_poll_file) \ + go/net/fd_poll_runtime.go \ go/net/port.go \ go/net/port_unix.go \ + go/net/race0.go \ $(go_net_sendfile_file) \ + go/net/singleflight.go \ go/net/sock_posix.go \ - go/net/sock_unix.go \ $(go_net_sock_file) \ go/net/sockopt_posix.go \ $(go_net_sockopt_file) \ $(go_net_sockoptip_file) \ go/net/tcpsock.go \ go/net/tcpsock_posix.go \ + go/net/tcpsockopt_posix.go \ + $(go_net_tcpsockopt_file) \ go/net/udpsock.go \ go/net/udpsock_posix.go \ go/net/unixsock.go \ @@ -1046,12 +1062,15 @@ go_net_files = \ @LIBGO_IS_386_TRUE@@LIBGO_IS_SOLARIS_TRUE@go_os_dir_file = go/os/dir_largefile.go @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_dir_file = go/os/dir_regfile.go @LIBGO_IS_LINUX_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_os_dir_file = go/os/dir_largefile.go +@LIBGO_IS_DARWIN_FALSE@go_os_getwd_file = +@LIBGO_IS_DARWIN_TRUE@go_os_getwd_file = go/os/getwd_darwin.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_bsd.go @LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_uname.go @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@@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_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 @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go @LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go @@ -1066,7 +1085,7 @@ go_os_files = \ go/os/doc.go \ go/os/env.go \ go/os/error.go \ - go/os/error_posix.go \ + go/os/error_unix.go \ go/os/exec.go \ go/os/exec_posix.go \ go/os/exec_unix.go \ @@ -1074,6 +1093,7 @@ go_os_files = \ go/os/file_posix.go \ go/os/file_unix.go \ go/os/getwd.go \ + $(go_os_getwd_file) \ go/os/path.go \ go/os/path_unix.go \ $(go_os_pipe_file) \ @@ -1149,7 +1169,11 @@ go_strings_files = \ go/strings/reader.go \ go/strings/replace.go \ go/strings/search.go \ - go/strings/strings.go + go/strings/strings.go \ + go/strings/strings_decl.go + +go_strings_c_files = \ + go/strings/indexbyte.c go_sync_files = \ go/sync/cond.go \ @@ -1173,6 +1197,7 @@ go_syslog_c_files = \ go_testing_files = \ go/testing/allocs.go \ go/testing/benchmark.go \ + go/testing/cover.go \ go/testing/example.go \ go/testing/testing.go @@ -1208,6 +1233,7 @@ go_archive_tar_files = \ go_archive_zip_files = \ go/archive/zip/reader.go \ + go/archive/zip/register.go \ go/archive/zip/struct.go \ go/archive/zip/writer.go @@ -1259,6 +1285,7 @@ go_crypto_cipher_files = \ go/crypto/cipher/cfb.go \ go/crypto/cipher/cipher.go \ go/crypto/cipher/ctr.go \ + go/crypto/cipher/gcm.go \ go/crypto/cipher/io.go \ go/crypto/cipher/ofb.go @@ -1275,7 +1302,8 @@ go_crypto_ecdsa_files = \ go_crypto_elliptic_files = \ go/crypto/elliptic/elliptic.go \ - go/crypto/elliptic/p224.go + go/crypto/elliptic/p224.go \ + go/crypto/elliptic/p256.go go_crypto_hmac_files = \ go/crypto/hmac/hmac.go @@ -1295,6 +1323,7 @@ go_crypto_rc4_files = \ go_crypto_rsa_files = \ go/crypto/rsa/pkcs1v15.go \ + go/crypto/rsa/pss.go \ go/crypto/rsa/rsa.go go_crypto_sha1_files = \ @@ -1509,11 +1538,15 @@ go_image_color_files = \ go/image/color/color.go \ go/image/color/ycbcr.go +go_image_color_palette_files = \ + go/image/color/palette/palette.go + go_image_draw_files = \ go/image/draw/draw.go go_image_gif_files = \ - go/image/gif/reader.go + go/image/gif/reader.go \ + go/image/gif/writer.go go_image_jpeg_files = \ go/image/jpeg/fdct.go \ @@ -1849,6 +1882,7 @@ libgo_go_objs = \ bytes.lo \ bytes/index.lo \ crypto.lo \ + encoding.lo \ errors.lo \ expvar.lo \ flag.lo \ @@ -1870,6 +1904,7 @@ libgo_go_objs = \ sort.lo \ strconv.lo \ strings.lo \ + strings/index.lo \ sync.lo \ syscall.lo \ syscall/errno.lo \ @@ -1946,6 +1981,7 @@ libgo_go_objs = \ net/http/httputil.lo \ net/http/pprof.lo \ image/color.lo \ + image/color/palette.lo \ image/draw.lo \ image/gif.lo \ image/jpeg.lo \ @@ -3518,6 +3554,26 @@ uninstall-toolexeclibgoimageDATA: test -n "$$files" || exit 0; \ echo " ( cd '$(DESTDIR)$(toolexeclibgoimagedir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(toolexeclibgoimagedir)" && rm -f $$files +install-toolexeclibgoimagecolorDATA: $(toolexeclibgoimagecolor_DATA) + @$(NORMAL_INSTALL) + test -z "$(toolexeclibgoimagecolordir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoimagecolordir)" + @list='$(toolexeclibgoimagecolor_DATA)'; test -n "$(toolexeclibgoimagecolordir)" || list=; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(toolexeclibgoimagecolordir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(toolexeclibgoimagecolordir)" || exit $$?; \ + done + +uninstall-toolexeclibgoimagecolorDATA: + @$(NORMAL_UNINSTALL) + @list='$(toolexeclibgoimagecolor_DATA)'; test -n "$(toolexeclibgoimagecolordir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + test -n "$$files" || exit 0; \ + echo " ( cd '$(DESTDIR)$(toolexeclibgoimagecolordir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(toolexeclibgoimagecolordir)" && rm -f $$files install-toolexeclibgoindexDATA: $(toolexeclibgoindex_DATA) @$(NORMAL_INSTALL) test -z "$(toolexeclibgoindexdir)" || $(MKDIR_P) "$(DESTDIR)$(toolexeclibgoindexdir)" @@ -4019,7 +4075,7 @@ all-am: Makefile $(LIBRARIES) $(LTLIBRARIES) all-multi $(DATA) \ config.h installdirs: installdirs-recursive installdirs-am: - for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodatabasedir)" "$(DESTDIR)$(toolexeclibgodatabasesqldir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \ + for dir in "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibdir)" "$(DESTDIR)$(toolexeclibgodir)" "$(DESTDIR)$(toolexeclibgoarchivedir)" "$(DESTDIR)$(toolexeclibgocompressdir)" "$(DESTDIR)$(toolexeclibgocontainerdir)" "$(DESTDIR)$(toolexeclibgocryptodir)" "$(DESTDIR)$(toolexeclibgocryptox509dir)" "$(DESTDIR)$(toolexeclibgodatabasedir)" "$(DESTDIR)$(toolexeclibgodatabasesqldir)" "$(DESTDIR)$(toolexeclibgodebugdir)" "$(DESTDIR)$(toolexeclibgoencodingdir)" "$(DESTDIR)$(toolexeclibgoexpdir)" "$(DESTDIR)$(toolexeclibgogodir)" "$(DESTDIR)$(toolexeclibgohashdir)" "$(DESTDIR)$(toolexeclibgohtmldir)" "$(DESTDIR)$(toolexeclibgoimagedir)" "$(DESTDIR)$(toolexeclibgoimagecolordir)" "$(DESTDIR)$(toolexeclibgoindexdir)" "$(DESTDIR)$(toolexeclibgoiodir)" "$(DESTDIR)$(toolexeclibgologdir)" "$(DESTDIR)$(toolexeclibgomathdir)" "$(DESTDIR)$(toolexeclibgomimedir)" "$(DESTDIR)$(toolexeclibgonetdir)" "$(DESTDIR)$(toolexeclibgonethttpdir)" "$(DESTDIR)$(toolexeclibgonetrpcdir)" "$(DESTDIR)$(toolexeclibgoolddir)" "$(DESTDIR)$(toolexeclibgoosdir)" "$(DESTDIR)$(toolexeclibgopathdir)" "$(DESTDIR)$(toolexeclibgoregexpdir)" "$(DESTDIR)$(toolexeclibgoruntimedir)" "$(DESTDIR)$(toolexeclibgosyncdir)" "$(DESTDIR)$(toolexeclibgotestingdir)" "$(DESTDIR)$(toolexeclibgotextdir)" "$(DESTDIR)$(toolexeclibgotexttemplatedir)" "$(DESTDIR)$(toolexeclibgounicodedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive @@ -4092,6 +4148,7 @@ install-exec-am: install-multi install-toolexeclibLIBRARIES \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ install-toolexeclibgogoDATA install-toolexeclibgohashDATA \ install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \ + install-toolexeclibgoimagecolorDATA \ install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \ install-toolexeclibgologDATA install-toolexeclibgomathDATA \ install-toolexeclibgomimeDATA install-toolexeclibgonetDATA \ @@ -4159,6 +4216,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \ uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgohtmlDATA \ uninstall-toolexeclibgoimageDATA \ + uninstall-toolexeclibgoimagecolorDATA \ uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \ uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \ uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \ @@ -4203,6 +4261,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \ install-toolexeclibgoencodingDATA install-toolexeclibgoexpDATA \ install-toolexeclibgogoDATA install-toolexeclibgohashDATA \ install-toolexeclibgohtmlDATA install-toolexeclibgoimageDATA \ + install-toolexeclibgoimagecolorDATA \ install-toolexeclibgoindexDATA install-toolexeclibgoioDATA \ install-toolexeclibgologDATA install-toolexeclibgomathDATA \ install-toolexeclibgomimeDATA install-toolexeclibgonetDATA \ @@ -4234,6 +4293,7 @@ uninstall-am: uninstall-toolexeclibLIBRARIES \ uninstall-toolexeclibgohashDATA \ uninstall-toolexeclibgohtmlDATA \ uninstall-toolexeclibgoimageDATA \ + uninstall-toolexeclibgoimagecolorDATA \ uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \ uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \ uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \ @@ -4391,6 +4451,15 @@ crypto/check: $(CHECK_DEPS) @$(CHECK) .PHONY: crypto/check +@go_include@ encoding.lo.dep +encoding.lo.dep: $(go_encoding_files) + $(BUILDDEPS) +encoding.lo: $(go_encoding_files) + $(BUILDPACKAGE) +encoding/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: encoding/check + @go_include@ errors.lo.dep errors.lo.dep: $(go_errors_files) $(BUILDDEPS) @@ -4572,6 +4641,9 @@ strings.lo.dep: $(go_strings_files) $(BUILDDEPS) strings.lo: $(go_strings_files) $(BUILDPACKAGE) +strings/index.lo: $(go_strings_c_files) + @$(MKDIR_P) strings + $(LTCOMPILE) -c -o strings/index.lo $(srcdir)/go/strings/indexbyte.c strings/check: $(CHECK_DEPS) @$(CHECK) .PHONY: strings/check @@ -5179,6 +5251,15 @@ image/color/check: $(CHECK_DEPS) @$(CHECK) .PHONY: image/color/check +@go_include@ image/color/palette.lo.dep +image/color/palette.lo.dep: $(go_image_color_palette_files) + $(BUILDDEPS) +image/color/palette.lo: $(go_image_color_palette_files) + $(BUILDPACKAGE) +image/color/palette/check: $(CHECK_DEPS) + @$(CHECK) +.PHONY: image/color/palette/check + @go_include@ image/draw.lo.dep image/draw.lo.dep: $(go_image_draw_files) $(BUILDDEPS) @@ -5586,6 +5667,8 @@ bytes.gox: bytes.lo $(BUILDGOX) crypto.gox: crypto.lo $(BUILDGOX) +encoding.gox: encoding.lo + $(BUILDGOX) errors.gox: errors.lo $(BUILDGOX) expvar.gox: expvar.lo @@ -5783,6 +5866,9 @@ image/jpeg.gox: image/jpeg.lo image/png.gox: image/png.lo $(BUILDGOX) +image/color/palette.gox: image/color/palette.lo + $(BUILDGOX) + index/suffixarray.gox: index/suffixarray.lo $(BUILDGOX) diff --git a/libgo/config.h.in b/libgo/config.h.in index f6da8b9..8af626d 100644 --- a/libgo/config.h.in +++ b/libgo/config.h.in @@ -147,6 +147,9 @@ /* Define to 1 if you have the `mknodat' function. */ #undef HAVE_MKNODAT +/* Define to 1 if you have the <netinet/icmp6.h> header file. */ +#undef HAVE_NETINET_ICMP6_H + /* Define to 1 if you have the <netinet/if_ether.h> header file. */ #undef HAVE_NETINET_IF_ETHER_H diff --git a/libgo/configure b/libgo/configure index 90cafb5..d2ad366 100755 --- a/libgo/configure +++ b/libgo/configure @@ -659,6 +659,8 @@ LIBGO_IS_SOLARIS_FALSE LIBGO_IS_SOLARIS_TRUE LIBGO_IS_RTEMS_FALSE LIBGO_IS_RTEMS_TRUE +LIBGO_IS_DRAGONFLY_FALSE +LIBGO_IS_DRAGONFLY_TRUE LIBGO_IS_OPENBSD_FALSE LIBGO_IS_OPENBSD_TRUE LIBGO_IS_NETBSD_FALSE @@ -11111,7 +11113,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11114 "configure" +#line 11116 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11217,7 +11219,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11220 "configure" +#line 11222 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -13490,6 +13492,7 @@ is_irix=no is_linux=no is_netbsd=no is_openbsd=no +is_dragonfly=no is_rtems=no is_solaris=no GOOS=unknown @@ -13500,6 +13503,7 @@ case ${host} in *-*-linux*) is_linux=yes; GOOS=linux ;; *-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;; *-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;; + *-*-dragonfly*) is_dragonfly=yes; GOOS=dragonfly ;; *-*-rtems*) is_rtems=yes; GOOS=rtems ;; *-*-solaris2*) is_solaris=yes; GOOS=solaris ;; esac @@ -13551,6 +13555,14 @@ else LIBGO_IS_OPENBSD_FALSE= fi + if test $is_dragonly = yes; then + LIBGO_IS_DRAGONFLY_TRUE= + LIBGO_IS_DRAGONFLY_FALSE='#' +else + LIBGO_IS_DRAGONFLY_TRUE='#' + LIBGO_IS_DRAGONFLY_FALSE= +fi + if test $is_rtems = yes; then LIBGO_IS_RTEMS_TRUE= LIBGO_IS_RTEMS_FALSE='#' @@ -14600,7 +14612,7 @@ no) ;; esac -for ac_header in sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h +for ac_header in sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -15502,6 +15514,10 @@ if test -z "${LIBGO_IS_OPENBSD_TRUE}" && test -z "${LIBGO_IS_OPENBSD_FALSE}"; th as_fn_error "conditional \"LIBGO_IS_OPENBSD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${LIBGO_IS_DRAGONFLY_TRUE}" && test -z "${LIBGO_IS_DRAGONFLY_FALSE}"; then + as_fn_error "conditional \"LIBGO_IS_DRAGONFLY\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${LIBGO_IS_RTEMS_TRUE}" && test -z "${LIBGO_IS_RTEMS_FALSE}"; then as_fn_error "conditional \"LIBGO_IS_RTEMS\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libgo/configure.ac b/libgo/configure.ac index f7b780b..1e84dc7 100644 --- a/libgo/configure.ac +++ b/libgo/configure.ac @@ -133,6 +133,7 @@ is_irix=no is_linux=no is_netbsd=no is_openbsd=no +is_dragonfly=no is_rtems=no is_solaris=no GOOS=unknown @@ -143,6 +144,7 @@ case ${host} in *-*-linux*) is_linux=yes; GOOS=linux ;; *-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;; *-*-openbsd*) is_openbsd=yes; GOOS=openbsd ;; + *-*-dragonfly*) is_dragonfly=yes; GOOS=dragonfly ;; *-*-rtems*) is_rtems=yes; GOOS=rtems ;; *-*-solaris2*) is_solaris=yes; GOOS=solaris ;; esac @@ -152,6 +154,7 @@ AM_CONDITIONAL(LIBGO_IS_IRIX, test $is_irix = yes) AM_CONDITIONAL(LIBGO_IS_LINUX, test $is_linux = yes) AM_CONDITIONAL(LIBGO_IS_NETBSD, test $is_netbsd = yes) AM_CONDITIONAL(LIBGO_IS_OPENBSD, test $is_openbsd = yes) +AM_CONDITIONAL(LIBGO_IS_DRAGONFLY, test $is_dragonly = yes) AM_CONDITIONAL(LIBGO_IS_RTEMS, test $is_rtems = yes) AM_CONDITIONAL(LIBGO_IS_SOLARIS, test $is_solaris = yes) AC_SUBST(GOOS) @@ -471,7 +474,7 @@ no) ;; esac -AC_CHECK_HEADERS(sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h) +AC_CHECK_HEADERS(sys/file.h sys/mman.h syscall.h sys/epoll.h sys/inotify.h sys/ptrace.h sys/syscall.h sys/user.h sys/utsname.h sys/select.h sys/socket.h net/if.h net/if_arp.h net/route.h netpacket/packet.h sys/prctl.h sys/mount.h sys/vfs.h sys/statfs.h sys/timex.h sys/sysinfo.h utime.h linux/ether.h linux/fs.h linux/reboot.h netinet/icmp6.h netinet/in_syst.h netinet/ip.h netinet/ip_mroute.h netinet/if_ether.h) AC_CHECK_HEADERS([linux/filter.h linux/if_addr.h linux/if_ether.h linux/if_tun.h linux/netlink.h linux/rtnetlink.h], [], [], [#ifdef HAVE_SYS_SOCKET_H diff --git a/libgo/go/archive/tar/common.go b/libgo/go/archive/tar/common.go index 60d207c..1b961e3 100644 --- a/libgo/go/archive/tar/common.go +++ b/libgo/go/archive/tar/common.go @@ -13,6 +13,7 @@ package tar import ( + "bytes" "errors" "fmt" "os" @@ -82,9 +83,9 @@ func (fi headerFileInfo) Sys() interface{} { return fi.h } // Name returns the base name of the file. func (fi headerFileInfo) Name() string { if fi.IsDir() { - return path.Clean(fi.h.Name) + return path.Base(path.Clean(fi.h.Name)) } - return fi.h.Name + return path.Base(fi.h.Name) } // Mode returns the permission and mode bits for the headerFileInfo. @@ -174,9 +175,29 @@ const ( c_ISSOCK = 0140000 // Socket ) +// Keywords for the PAX Extended Header +const ( + paxAtime = "atime" + paxCharset = "charset" + paxComment = "comment" + paxCtime = "ctime" // please note that ctime is not a valid pax header. + paxGid = "gid" + paxGname = "gname" + paxLinkpath = "linkpath" + paxMtime = "mtime" + paxPath = "path" + paxSize = "size" + paxUid = "uid" + paxUname = "uname" + paxNone = "" +) + // FileInfoHeader creates a partially-populated Header from fi. // If fi describes a symlink, FileInfoHeader records link as the link target. // If fi describes a directory, a slash is appended to the name. +// Because os.FileInfo's Name method returns only the base name of +// the file it describes, it may be necessary to modify the Name field +// of the returned header to provide the full path name of the file. func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { if fi == nil { return nil, errors.New("tar: FileInfo is nil") @@ -257,3 +278,25 @@ func (sp *slicer) next(n int) (b []byte) { b, *sp = s[0:n], s[n:] return } + +func isASCII(s string) bool { + for _, c := range s { + if c >= 0x80 { + return false + } + } + return true +} + +func toASCII(s string) string { + if isASCII(s) { + return s + } + var buf bytes.Buffer + for _, c := range s { + if c < 0x80 { + buf.WriteByte(byte(c)) + } + } + return buf.String() +} diff --git a/libgo/go/archive/tar/reader.go b/libgo/go/archive/tar/reader.go index 05f82a4..b2d62f3 100644 --- a/libgo/go/archive/tar/reader.go +++ b/libgo/go/archive/tar/reader.go @@ -95,45 +95,45 @@ func (tr *Reader) Next() (*Header, error) { func mergePAX(hdr *Header, headers map[string]string) error { for k, v := range headers { switch k { - case "path": + case paxPath: hdr.Name = v - case "linkpath": + case paxLinkpath: hdr.Linkname = v - case "gname": + case paxGname: hdr.Gname = v - case "uname": + case paxUname: hdr.Uname = v - case "uid": + case paxUid: uid, err := strconv.ParseInt(v, 10, 0) if err != nil { return err } hdr.Uid = int(uid) - case "gid": + case paxGid: gid, err := strconv.ParseInt(v, 10, 0) if err != nil { return err } hdr.Gid = int(gid) - case "atime": + case paxAtime: t, err := parsePAXTime(v) if err != nil { return err } hdr.AccessTime = t - case "mtime": + case paxMtime: t, err := parsePAXTime(v) if err != nil { return err } hdr.ModTime = t - case "ctime": + case paxCtime: t, err := parsePAXTime(v) if err != nil { return err } hdr.ChangeTime = t - case "size": + case paxSize: size, err := strconv.ParseInt(v, 10, 0) if err != nil { return err @@ -243,13 +243,15 @@ func (tr *Reader) octal(b []byte) int64 { return x } - // Removing leading spaces. - for len(b) > 0 && b[0] == ' ' { - b = b[1:] - } - // Removing trailing NULs and spaces. - for len(b) > 0 && (b[len(b)-1] == ' ' || b[len(b)-1] == '\x00') { - b = b[0 : len(b)-1] + // Because unused fields are filled with NULs, we need + // to skip leading NULs. Fields may also be padded with + // spaces or NULs. + // So we remove leading and trailing NULs and spaces to + // be sure. + b = bytes.Trim(b, " \x00") + + if len(b) == 0 { + return 0 } x, err := strconv.ParseUint(cString(b), 8, 64) if err != nil { diff --git a/libgo/go/archive/tar/reader_test.go b/libgo/go/archive/tar/reader_test.go index 9a19682..1285616 100644 --- a/libgo/go/archive/tar/reader_test.go +++ b/libgo/go/archive/tar/reader_test.go @@ -142,6 +142,25 @@ var untarTests = []*untarTest{ }, }, }, + { + file: "testdata/nil-uid.tar", // golang.org/issue/5290 + headers: []*Header{ + { + Name: "P1050238.JPG.log", + Mode: 0664, + Uid: 0, + Gid: 0, + Size: 14, + ModTime: time.Unix(1365454838, 0), + Typeflag: TypeReg, + Linkname: "", + Uname: "eyefi", + Gname: "eyefi", + Devmajor: 0, + Devminor: 0, + }, + }, + }, } func TestReader(t *testing.T) { @@ -152,6 +171,7 @@ testLoop: t.Errorf("test %d: Unexpected error: %v", i, err) continue } + defer f.Close() tr := NewReader(f) for j, header := range test.headers { hdr, err := tr.Next() @@ -172,7 +192,6 @@ testLoop: if hdr != nil || err != nil { t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err) } - f.Close() } } diff --git a/libgo/go/archive/tar/tar_test.go b/libgo/go/archive/tar/tar_test.go index dd63103..616a9cc 100644 --- a/libgo/go/archive/tar/tar_test.go +++ b/libgo/go/archive/tar/tar_test.go @@ -8,7 +8,9 @@ import ( "bytes" "io/ioutil" "os" + "path" "reflect" + "strings" "testing" "time" ) @@ -249,7 +251,14 @@ func TestHeaderRoundTrip(t *testing.T) { t.Error(err) continue } - if got, want := h2.Name, g.h.Name; got != want { + if strings.Contains(fi.Name(), "/") { + t.Errorf("FileInfo of %q contains slash: %q", g.h.Name, fi.Name()) + } + name := path.Base(g.h.Name) + if fi.IsDir() { + name += "/" + } + if got, want := h2.Name, name; got != want { t.Errorf("i=%d: Name: got %v, want %v", i, got, want) } if got, want := h2.Size, g.h.Size; got != want { diff --git a/libgo/go/archive/tar/testdata/nil-uid.tar b/libgo/go/archive/tar/testdata/nil-uid.tar Binary files differnew file mode 100644 index 0000000..cc9cfaa --- /dev/null +++ b/libgo/go/archive/tar/testdata/nil-uid.tar diff --git a/libgo/go/archive/tar/writer.go b/libgo/go/archive/tar/writer.go index d92dd06..549f146 100644 --- a/libgo/go/archive/tar/writer.go +++ b/libgo/go/archive/tar/writer.go @@ -24,6 +24,7 @@ var ( ErrFieldTooLong = errors.New("archive/tar: header field too long") ErrWriteAfterClose = errors.New("archive/tar: write after close") errNameTooLong = errors.New("archive/tar: name too long") + errInvalidHeader = errors.New("archive/tar: header field too long or contains invalid values") ) // A Writer provides sequential writing of a tar archive in POSIX.1 format. @@ -37,6 +38,7 @@ type Writer struct { pad int64 // amount of padding to write after current file entry closed bool usedBinary bool // whether the binary numeric field extension was used + preferPax bool // use pax header instead of binary numeric header } // NewWriter creates a new Writer writing to w. @@ -65,16 +67,23 @@ func (tw *Writer) Flush() error { } // Write s into b, terminating it with a NUL if there is room. -func (tw *Writer) cString(b []byte, s string) { +// If the value is too long for the field and allowPax is true add a paxheader record instead +func (tw *Writer) cString(b []byte, s string, allowPax bool, paxKeyword string, paxHeaders map[string]string) { + needsPaxHeader := allowPax && len(s) > len(b) || !isASCII(s) + if needsPaxHeader { + paxHeaders[paxKeyword] = s + return + } if len(s) > len(b) { if tw.err == nil { tw.err = ErrFieldTooLong } return } - copy(b, s) - if len(s) < len(b) { - b[len(s)] = 0 + ascii := toASCII(s) + copy(b, ascii) + if len(ascii) < len(b) { + b[len(ascii)] = 0 } } @@ -85,17 +94,27 @@ func (tw *Writer) octal(b []byte, x int64) { for len(s)+1 < len(b) { s = "0" + s } - tw.cString(b, s) + tw.cString(b, s, false, paxNone, nil) } // Write x into b, either as octal or as binary (GNUtar/star extension). -func (tw *Writer) numeric(b []byte, x int64) { +// If the value is too long for the field and writingPax is enabled both for the field and the add a paxheader record instead +func (tw *Writer) numeric(b []byte, x int64, allowPax bool, paxKeyword string, paxHeaders map[string]string) { // Try octal first. s := strconv.FormatInt(x, 8) if len(s) < len(b) { tw.octal(b, x) return } + + // If it is too long for octal, and pax is preferred, use a pax header + if allowPax && tw.preferPax { + tw.octal(b, 0) + s := strconv.FormatInt(x, 10) + paxHeaders[paxKeyword] = s + return + } + // Too big: use binary (big-endian). tw.usedBinary = true for i := len(b) - 1; x > 0 && i >= 0; i-- { @@ -115,6 +134,15 @@ var ( // WriteHeader calls Flush if it is not the first header. // Calling after a Close will return ErrWriteAfterClose. func (tw *Writer) WriteHeader(hdr *Header) error { + return tw.writeHeader(hdr, true) +} + +// WriteHeader writes hdr and prepares to accept the file's contents. +// WriteHeader calls Flush if it is not the first header. +// Calling after a Close will return ErrWriteAfterClose. +// As this method is called internally by writePax header to allow it to +// suppress writing the pax header. +func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error { if tw.closed { return ErrWriteAfterClose } @@ -124,31 +152,21 @@ func (tw *Writer) WriteHeader(hdr *Header) error { if tw.err != nil { return tw.err } - // Decide whether or not to use PAX extensions + + // a map to hold pax header records, if any are needed + paxHeaders := make(map[string]string) + // TODO(shanemhansen): we might want to use PAX headers for // subsecond time resolution, but for now let's just capture - // the long name/long symlink use case. - suffix := hdr.Name - prefix := "" - if len(hdr.Name) > fileNameSize || len(hdr.Linkname) > fileNameSize { - var err error - prefix, suffix, err = tw.splitUSTARLongName(hdr.Name) - // Either we were unable to pack the long name into ustar format - // or the link name is too long; use PAX headers. - if err == errNameTooLong || len(hdr.Linkname) > fileNameSize { - if err := tw.writePAXHeader(hdr); err != nil { - return err - } - } else if err != nil { - return err - } - } - tw.nb = int64(hdr.Size) - tw.pad = -tw.nb & (blockSize - 1) // blockSize is a power of two + // too long fields or non ascii characters header := make([]byte, blockSize) s := slicer(header) - tw.cString(s.next(fileNameSize), suffix) + + // keep a reference to the filename to allow to overwrite it later if we detect that we can use ustar longnames instead of pax + pathHeaderBytes := s.next(fileNameSize) + + tw.cString(pathHeaderBytes, hdr.Name, true, paxPath, paxHeaders) // Handle out of range ModTime carefully. var modTime int64 @@ -156,27 +174,55 @@ func (tw *Writer) WriteHeader(hdr *Header) error { modTime = hdr.ModTime.Unix() } - tw.octal(s.next(8), hdr.Mode) // 100:108 - tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 - tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 - tw.numeric(s.next(12), hdr.Size) // 124:136 - tw.numeric(s.next(12), modTime) // 136:148 - s.next(8) // chksum (148:156) - s.next(1)[0] = hdr.Typeflag // 156:157 - tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) - copy(s.next(8), []byte("ustar\x0000")) // 257:265 - tw.cString(s.next(32), hdr.Uname) // 265:297 - tw.cString(s.next(32), hdr.Gname) // 297:329 - tw.numeric(s.next(8), hdr.Devmajor) // 329:337 - tw.numeric(s.next(8), hdr.Devminor) // 337:345 - tw.cString(s.next(155), prefix) // 345:500 + tw.octal(s.next(8), hdr.Mode) // 100:108 + tw.numeric(s.next(8), int64(hdr.Uid), true, paxUid, paxHeaders) // 108:116 + tw.numeric(s.next(8), int64(hdr.Gid), true, paxGid, paxHeaders) // 116:124 + tw.numeric(s.next(12), hdr.Size, true, paxSize, paxHeaders) // 124:136 + tw.numeric(s.next(12), modTime, false, paxNone, nil) // 136:148 --- consider using pax for finer granularity + s.next(8) // chksum (148:156) + s.next(1)[0] = hdr.Typeflag // 156:157 + + tw.cString(s.next(100), hdr.Linkname, true, paxLinkpath, paxHeaders) + + copy(s.next(8), []byte("ustar\x0000")) // 257:265 + tw.cString(s.next(32), hdr.Uname, true, paxUname, paxHeaders) // 265:297 + tw.cString(s.next(32), hdr.Gname, true, paxGname, paxHeaders) // 297:329 + tw.numeric(s.next(8), hdr.Devmajor, false, paxNone, nil) // 329:337 + tw.numeric(s.next(8), hdr.Devminor, false, paxNone, nil) // 337:345 + + // keep a reference to the prefix to allow to overwrite it later if we detect that we can use ustar longnames instead of pax + prefixHeaderBytes := s.next(155) + tw.cString(prefixHeaderBytes, "", false, paxNone, nil) // 345:500 prefix + // Use the GNU magic instead of POSIX magic if we used any GNU extensions. if tw.usedBinary { copy(header[257:265], []byte("ustar \x00")) } - // Use the ustar magic if we used ustar long names. - if len(prefix) > 0 { - copy(header[257:265], []byte("ustar\000")) + + _, paxPathUsed := paxHeaders[paxPath] + // try to use a ustar header when only the name is too long + if !tw.preferPax && len(paxHeaders) == 1 && paxPathUsed { + suffix := hdr.Name + prefix := "" + if len(hdr.Name) > fileNameSize && isASCII(hdr.Name) { + var err error + prefix, suffix, err = tw.splitUSTARLongName(hdr.Name) + if err == nil { + // ok we can use a ustar long name instead of pax, now correct the fields + + // remove the path field from the pax header. this will suppress the pax header + delete(paxHeaders, paxPath) + + // update the path fields + tw.cString(pathHeaderBytes, suffix, false, paxNone, nil) + 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")) + } + } + } } // The chksum field is terminated by a NUL and a space. @@ -190,8 +236,18 @@ func (tw *Writer) WriteHeader(hdr *Header) error { return tw.err } - _, tw.err = tw.w.Write(header) + if len(paxHeaders) > 0 { + if !allowPax { + return errInvalidHeader + } + if err := tw.writePAXHeader(hdr, paxHeaders); err != nil { + return err + } + } + tw.nb = int64(hdr.Size) + tw.pad = (blockSize - (tw.nb % blockSize)) % blockSize + _, tw.err = tw.w.Write(header) return tw.err } @@ -207,8 +263,11 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er length-- } i := strings.LastIndex(name[:length], "/") - nlen := length - i - 1 - if i <= 0 || nlen > fileNameSize || nlen == 0 { + // nlen contains the resulting length in the name field. + // plen contains the resulting length in the prefix field. + nlen := len(name) - i - 1 + plen := i + if i <= 0 || nlen > fileNameSize || nlen == 0 || plen > fileNamePrefixSize { err = errNameTooLong return } @@ -218,7 +277,7 @@ func (tw *Writer) splitUSTARLongName(name string) (prefix, suffix string, err er // writePaxHeader writes an extended pax header to the // archive. -func (tw *Writer) writePAXHeader(hdr *Header) error { +func (tw *Writer) writePAXHeader(hdr *Header, paxHeaders map[string]string) error { // Prepare extended header ext := new(Header) ext.Typeflag = TypeXHeader @@ -229,18 +288,23 @@ func (tw *Writer) writePAXHeader(hdr *Header) error { // with the current pid. pid := os.Getpid() dir, file := path.Split(hdr.Name) - ext.Name = path.Join(dir, - fmt.Sprintf("PaxHeaders.%d", pid), file)[0:100] + fullName := path.Join(dir, + fmt.Sprintf("PaxHeaders.%d", pid), file) + + ascii := toASCII(fullName) + if len(ascii) > 100 { + ascii = ascii[:100] + } + ext.Name = ascii // Construct the body var buf bytes.Buffer - if len(hdr.Name) > fileNameSize { - fmt.Fprint(&buf, paxHeader("path="+hdr.Name)) - } - if len(hdr.Linkname) > fileNameSize { - fmt.Fprint(&buf, paxHeader("linkpath="+hdr.Linkname)) + + for k, v := range paxHeaders { + fmt.Fprint(&buf, paxHeader(k+"="+v)) } + ext.Size = int64(len(buf.Bytes())) - if err := tw.WriteHeader(ext); err != nil { + if err := tw.writeHeader(ext, false); err != nil { return err } if _, err := tw.Write(buf.Bytes()); err != nil { diff --git a/libgo/go/archive/tar/writer_test.go b/libgo/go/archive/tar/writer_test.go index 4cf7c72..30ebf97 100644 --- a/libgo/go/archive/tar/writer_test.go +++ b/libgo/go/archive/tar/writer_test.go @@ -243,15 +243,110 @@ func TestPax(t *testing.T) { } } +func TestPaxSymlink(t *testing.T) { + // Create an archive with a large linkname + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + hdr.Typeflag = TypeSymlink + if err != nil { + t.Fatalf("os.Stat:1 %v", err) + } + // Force a PAX long linkname to be written + longLinkname := strings.Repeat("1234567890/1234567890", 10) + hdr.Linkname = longLinkname + + hdr.Size = 0 + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Simple test to make sure PAX extensions are in effect + if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { + t.Fatal("Expected at least one PAX header to be written.") + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Linkname != longLinkname { + t.Fatal("Couldn't recover long link name") + } +} + +func TestPaxNonAscii(t *testing.T) { + // Create an archive with non ascii. These should trigger a pax header + // because pax headers have a defined utf-8 encoding. + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + + hdr, err := FileInfoHeader(fileinfo, "") + if err != nil { + t.Fatalf("os.Stat:1 %v", err) + } + + // some sample data + chineseFilename := "文件名" + chineseGroupname := "組" + chineseUsername := "用戶名" + + hdr.Name = chineseFilename + hdr.Gname = chineseGroupname + hdr.Uname = chineseUsername + + contents := strings.Repeat(" ", int(hdr.Size)) + + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if _, err = writer.Write([]byte(contents)); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Simple test to make sure PAX extensions are in effect + if !bytes.Contains(buf.Bytes(), []byte("PaxHeaders.")) { + t.Fatal("Expected at least one PAX header to be written.") + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Name != chineseFilename { + t.Fatal("Couldn't recover unicode name") + } + if hdr.Gname != chineseGroupname { + t.Fatal("Couldn't recover unicode group") + } + if hdr.Uname != chineseUsername { + t.Fatal("Couldn't recover unicode user") + } +} + func TestPAXHeader(t *testing.T) { medName := strings.Repeat("CD", 50) longName := strings.Repeat("AB", 100) paxTests := [][2]string{ - {"name=/etc/hosts", "19 name=/etc/hosts\n"}, + {paxPath + "=/etc/hosts", "19 path=/etc/hosts\n"}, {"a=b", "6 a=b\n"}, // Single digit length {"a=names", "11 a=names\n"}, // Test case involving carries - {"name=" + longName, fmt.Sprintf("210 name=%s\n", longName)}, - {"name=" + medName, fmt.Sprintf("110 name=%s\n", medName)}} + {paxPath + "=" + longName, fmt.Sprintf("210 path=%s\n", longName)}, + {paxPath + "=" + medName, fmt.Sprintf("110 path=%s\n", medName)}} for _, test := range paxTests { key, expected := test[0], test[1] @@ -260,3 +355,39 @@ func TestPAXHeader(t *testing.T) { } } } + +func TestUSTARLongName(t *testing.T) { + // Create an archive with a path that failed to split with USTAR extension in previous versions. + fileinfo, err := os.Stat("testdata/small.txt") + if err != nil { + t.Fatal(err) + } + hdr, err := FileInfoHeader(fileinfo, "") + hdr.Typeflag = TypeDir + if err != nil { + t.Fatalf("os.Stat:1 %v", err) + } + // Force a PAX long name to be written. The name was taken from a practical example + // that fails and replaced ever char through numbers to anonymize the sample. + longName := "/0000_0000000/00000-000000000/0000_0000000/00000-0000000000000/0000_0000000/00000-0000000-00000000/0000_0000000/00000000/0000_0000000/000/0000_0000000/00000000v00/0000_0000000/000000/0000_0000000/0000000/0000_0000000/00000y-00/0000/0000/00000000/0x000000/" + hdr.Name = longName + + hdr.Size = 0 + var buf bytes.Buffer + writer := NewWriter(&buf) + if err := writer.WriteHeader(hdr); err != nil { + t.Fatal(err) + } + if err := writer.Close(); err != nil { + t.Fatal(err) + } + // Test that we can get a long name back out of the archive. + reader := NewReader(&buf) + hdr, err = reader.Next() + if err != nil { + t.Fatal(err) + } + if hdr.Name != longName { + t.Fatal("Couldn't recover long name") + } +} diff --git a/libgo/go/archive/zip/reader.go b/libgo/go/archive/zip/reader.go index f19cf2d..1167373 100644 --- a/libgo/go/archive/zip/reader.go +++ b/libgo/go/archive/zip/reader.go @@ -6,13 +6,11 @@ package zip import ( "bufio" - "compress/flate" "encoding/binary" "errors" "hash" "hash/crc32" "io" - "io/ioutil" "os" ) @@ -116,6 +114,19 @@ func (rc *ReadCloser) Close() error { return rc.f.Close() } +// DataOffset returns the offset of the file's possibly-compressed +// data, relative to the beginning of the zip file. +// +// Most callers should instead use Open, which transparently +// decompresses data and verifies checksums. +func (f *File) DataOffset() (offset int64, err error) { + bodyOffset, err := f.findBodyOffset() + if err != nil { + return + } + return f.headerOffset + bodyOffset, nil +} + // Open returns a ReadCloser that provides access to the File's contents. // Multiple files may be read concurrently. func (f *File) Open() (rc io.ReadCloser, err error) { @@ -125,15 +136,12 @@ func (f *File) Open() (rc io.ReadCloser, err error) { } size := int64(f.CompressedSize64) r := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) - switch f.Method { - case Store: // (no compression) - rc = ioutil.NopCloser(r) - case Deflate: - rc = flate.NewReader(r) - default: + dcomp := decompressor(f.Method) + if dcomp == nil { err = ErrAlgorithm return } + rc = dcomp(r) var desr io.Reader if f.hasDataDescriptor() { desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) @@ -184,9 +192,8 @@ func (r *checksumReader) Close() error { return r.rc.Close() } // findBodyOffset does the minimum work to verify the file has a header // and returns the file body offset. func (f *File) findBodyOffset() (int64, error) { - r := io.NewSectionReader(f.zipr, f.headerOffset, f.zipsize-f.headerOffset) var buf [fileHeaderLen]byte - if _, err := io.ReadFull(r, buf[:]); err != nil { + if _, err := f.zipr.ReadAt(buf[:], f.headerOffset); err != nil { return 0, err } b := readBuf(buf[:]) diff --git a/libgo/go/archive/zip/reader_test.go b/libgo/go/archive/zip/reader_test.go index 833ba28..78875ec 100644 --- a/libgo/go/archive/zip/reader_test.go +++ b/libgo/go/archive/zip/reader_test.go @@ -276,6 +276,7 @@ func readTestZip(t *testing.T, zt ZipTest) { var rc *ReadCloser rc, err = OpenReader(filepath.Join("testdata", zt.Name)) if err == nil { + defer rc.Close() z = &rc.Reader } } diff --git a/libgo/go/archive/zip/register.go b/libgo/go/archive/zip/register.go new file mode 100644 index 0000000..c046f08 --- /dev/null +++ b/libgo/go/archive/zip/register.go @@ -0,0 +1,71 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package zip + +import ( + "compress/flate" + "io" + "io/ioutil" + "sync" +) + +// A Compressor returns a compressing writer, writing to the +// provided writer. On Close, any pending data should be flushed. +type Compressor func(io.Writer) (io.WriteCloser, error) + +// Decompressor is a function that wraps a Reader with a decompressing Reader. +// The decompressed ReadCloser is returned to callers who open files from +// within the archive. These callers are responsible for closing this reader +// when they're finished reading. +type Decompressor func(io.Reader) io.ReadCloser + +var ( + mu sync.RWMutex // guards compressor and decompressor maps + + compressors = map[uint16]Compressor{ + Store: func(w io.Writer) (io.WriteCloser, error) { return &nopCloser{w}, nil }, + Deflate: func(w io.Writer) (io.WriteCloser, error) { return flate.NewWriter(w, 5) }, + } + + decompressors = map[uint16]Decompressor{ + Store: ioutil.NopCloser, + Deflate: flate.NewReader, + } +) + +// RegisterDecompressor allows custom decompressors for a specified method ID. +func RegisterDecompressor(method uint16, d Decompressor) { + mu.Lock() + defer mu.Unlock() + + if _, ok := decompressors[method]; ok { + panic("decompressor already registered") + } + decompressors[method] = d +} + +// RegisterCompressor registers custom compressors for a specified method ID. +// The common methods Store and Deflate are built in. +func RegisterCompressor(method uint16, comp Compressor) { + mu.Lock() + defer mu.Unlock() + + if _, ok := compressors[method]; ok { + panic("compressor already registered") + } + compressors[method] = comp +} + +func compressor(method uint16) Compressor { + mu.RLock() + defer mu.RUnlock() + return compressors[method] +} + +func decompressor(method uint16) Decompressor { + mu.RLock() + defer mu.RUnlock() + return decompressors[method] +} diff --git a/libgo/go/archive/zip/struct.go b/libgo/go/archive/zip/struct.go index 73972d4..65e5238 100644 --- a/libgo/go/archive/zip/struct.go +++ b/libgo/go/archive/zip/struct.go @@ -21,6 +21,7 @@ package zip import ( "os" + "path" "time" ) @@ -99,7 +100,7 @@ type headerFileInfo struct { fh *FileHeader } -func (fi headerFileInfo) Name() string { return fi.fh.Name } +func (fi headerFileInfo) Name() string { return path.Base(fi.fh.Name) } func (fi headerFileInfo) Size() int64 { if fi.fh.UncompressedSize64 > 0 { return int64(fi.fh.UncompressedSize64) @@ -113,6 +114,9 @@ func (fi headerFileInfo) Sys() interface{} { return fi.fh } // FileInfoHeader creates a partially-populated FileHeader from an // os.FileInfo. +// Because os.FileInfo's Name method returns only the base name of +// the file it describes, it may be necessary to modify the Name field +// of the returned header to provide the full path name of the file. func FileInfoHeader(fi os.FileInfo) (*FileHeader, error) { size := fi.Size() fh := &FileHeader{ diff --git a/libgo/go/archive/zip/writer.go b/libgo/go/archive/zip/writer.go index e9f147c..6c9800a 100644 --- a/libgo/go/archive/zip/writer.go +++ b/libgo/go/archive/zip/writer.go @@ -6,7 +6,6 @@ package zip import ( "bufio" - "compress/flate" "encoding/binary" "errors" "hash" @@ -198,18 +197,15 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { compCount: &countWriter{w: w.cw}, crc32: crc32.NewIEEE(), } - switch fh.Method { - case Store: - fw.comp = nopCloser{fw.compCount} - case Deflate: - var err error - fw.comp, err = flate.NewWriter(fw.compCount, 5) - if err != nil { - return nil, err - } - default: + comp := compressor(fh.Method) + if comp == nil { return nil, ErrAlgorithm } + var err error + fw.comp, err = comp(fw.compCount) + if err != nil { + return nil, err + } fw.rawCount = &countWriter{w: fw.comp} h := &header{ diff --git a/libgo/go/archive/zip/zip_test.go b/libgo/go/archive/zip/zip_test.go index a8af206..32a16a7 100644 --- a/libgo/go/archive/zip/zip_test.go +++ b/libgo/go/archive/zip/zip_test.go @@ -9,22 +9,24 @@ package zip import ( "bytes" "fmt" + "hash" "io" "io/ioutil" + "sort" "strings" "testing" "time" ) func TestOver65kFiles(t *testing.T) { - if testing.Short() { - t.Skip("slow test; skipping") - } buf := new(bytes.Buffer) w := NewWriter(buf) const nFiles = (1 << 16) + 42 for i := 0; i < nFiles; i++ { - _, err := w.Create(fmt.Sprintf("%d.dat", i)) + _, err := w.CreateHeader(&FileHeader{ + Name: fmt.Sprintf("%d.dat", i), + Method: Store, // avoid Issue 6136 and Issue 6138 + }) if err != nil { t.Fatalf("creating file %d: %v", i, err) } @@ -105,29 +107,156 @@ func TestFileHeaderRoundTrip64(t *testing.T) { testHeaderRoundTrip(fh, uint32max, fh.UncompressedSize64, t) } +type repeatedByte struct { + off int64 + b byte + n int64 +} + +// rleBuffer is a run-length-encoded byte buffer. +// It's an io.Writer (like a bytes.Buffer) and also an io.ReaderAt, +// allowing random-access reads. +type rleBuffer struct { + buf []repeatedByte +} + +func (r *rleBuffer) Size() int64 { + if len(r.buf) == 0 { + return 0 + } + last := &r.buf[len(r.buf)-1] + return last.off + last.n +} + +func (r *rleBuffer) Write(p []byte) (n int, err error) { + var rp *repeatedByte + if len(r.buf) > 0 { + rp = &r.buf[len(r.buf)-1] + // Fast path, if p is entirely the same byte repeated. + if lastByte := rp.b; len(p) > 0 && p[0] == lastByte { + all := true + for _, b := range p { + if b != lastByte { + all = false + break + } + } + if all { + rp.n += int64(len(p)) + return len(p), nil + } + } + } + + for _, b := range p { + if rp == nil || rp.b != b { + r.buf = append(r.buf, repeatedByte{r.Size(), b, 1}) + rp = &r.buf[len(r.buf)-1] + } else { + rp.n++ + } + } + return len(p), nil +} + +func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) { + if len(p) == 0 { + return + } + skipParts := sort.Search(len(r.buf), func(i int) bool { + part := &r.buf[i] + return part.off+part.n > off + }) + parts := r.buf[skipParts:] + if len(parts) > 0 { + skipBytes := off - parts[0].off + for len(parts) > 0 { + part := parts[0] + for i := skipBytes; i < part.n; i++ { + if n == len(p) { + return + } + p[n] = part.b + n++ + } + parts = parts[1:] + skipBytes = 0 + } + } + if n != len(p) { + err = io.ErrUnexpectedEOF + } + return +} + +// Just testing the rleBuffer used in the Zip64 test above. Not used by the zip code. +func TestRLEBuffer(t *testing.T) { + b := new(rleBuffer) + var all []byte + writes := []string{"abcdeee", "eeeeeee", "eeeefghaaiii"} + for _, w := range writes { + b.Write([]byte(w)) + all = append(all, w...) + } + if len(b.buf) != 10 { + t.Fatalf("len(b.buf) = %d; want 10", len(b.buf)) + } + + for i := 0; i < len(all); i++ { + for j := 0; j < len(all)-i; j++ { + buf := make([]byte, j) + n, err := b.ReadAt(buf, int64(i)) + if err != nil || n != len(buf) { + t.Errorf("ReadAt(%d, %d) = %d, %v; want %d, nil", i, j, n, err, len(buf)) + } + if !bytes.Equal(buf, all[i:i+j]) { + t.Errorf("ReadAt(%d, %d) = %q; want %q", i, j, buf, all[i:i+j]) + } + } + } +} + +// fakeHash32 is a dummy Hash32 that always returns 0. +type fakeHash32 struct { + hash.Hash32 +} + +func (fakeHash32) Write(p []byte) (int, error) { return len(p), nil } +func (fakeHash32) Sum32() uint32 { return 0 } + func TestZip64(t *testing.T) { if testing.Short() { t.Skip("slow test; skipping") } + const size = 1 << 32 // before the "END\n" part + testZip64(t, size) +} + +func testZip64(t testing.TB, size int64) { + const chunkSize = 1024 + chunks := int(size / chunkSize) // write 2^32 bytes plus "END\n" to a zip file - buf := new(bytes.Buffer) + buf := new(rleBuffer) w := NewWriter(buf) - f, err := w.Create("huge.txt") + f, err := w.CreateHeader(&FileHeader{ + Name: "huge.txt", + Method: Store, + }) if err != nil { t.Fatal(err) } - chunk := make([]byte, 1024) + f.(*fileWriter).crc32 = fakeHash32{} + chunk := make([]byte, chunkSize) for i := range chunk { chunk[i] = '.' } - chunk[len(chunk)-1] = '\n' - end := []byte("END\n") - for i := 0; i < (1<<32)/1024; i++ { + for i := 0; i < chunks; i++ { _, err := f.Write(chunk) if err != nil { t.Fatal("write chunk:", err) } } + end := []byte("END\n") _, err = f.Write(end) if err != nil { t.Fatal("write end:", err) @@ -137,7 +266,7 @@ func TestZip64(t *testing.T) { } // read back zip file and check that we get to the end of it - r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len())) + r, err := NewReader(buf, int64(buf.Size())) if err != nil { t.Fatal("reader:", err) } @@ -146,7 +275,8 @@ func TestZip64(t *testing.T) { if err != nil { t.Fatal("opening:", err) } - for i := 0; i < (1<<32)/1024; i++ { + rc.(*checksumReader).hash = fakeHash32{} + for i := 0; i < chunks; i++ { _, err := io.ReadFull(rc, chunk) if err != nil { t.Fatal("read:", err) @@ -163,11 +293,13 @@ func TestZip64(t *testing.T) { if err != nil { t.Fatal("closing:", err) } - if got, want := f0.UncompressedSize, uint32(uint32max); got != want { - t.Errorf("UncompressedSize %d, want %d", got, want) + if size == 1<<32 { + if got, want := f0.UncompressedSize, uint32(uint32max); got != want { + t.Errorf("UncompressedSize %d, want %d", got, want) + } } - if got, want := f0.UncompressedSize64, (1<<32)+uint64(len(end)); got != want { + if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want { t.Errorf("UncompressedSize64 %d, want %d", got, want) } } @@ -253,3 +385,11 @@ func TestZeroLengthHeader(t *testing.T) { } testValidHeader(&h, t) } + +// Just benchmarking how fast the Zip64 test above is. Not related to +// our zip performance, since the test above disabled CRC32 and flate. +func BenchmarkZip64Test(b *testing.B) { + for i := 0; i < b.N; i++ { + testZip64(b, 1<<26) + } +} diff --git a/libgo/go/bufio/bufio.go b/libgo/go/bufio/bufio.go index df3501f..d1ff3c9 100644 --- a/libgo/go/bufio/bufio.go +++ b/libgo/go/bufio/bufio.go @@ -51,12 +51,9 @@ func NewReaderSize(rd io.Reader, size int) *Reader { if size < minReadBufferSize { size = minReadBufferSize } - return &Reader{ - buf: make([]byte, size), - rd: rd, - lastByte: -1, - lastRuneSize: -1, - } + r := new(Reader) + r.reset(make([]byte, size), rd) + return r } // NewReader returns a new Reader whose buffer has the default size. @@ -64,6 +61,21 @@ func NewReader(rd io.Reader) *Reader { return NewReaderSize(rd, defaultBufSize) } +// Reset discards any buffered data, resets all state, and switches +// the buffered reader to read from r. +func (b *Reader) Reset(r io.Reader) { + b.reset(b.buf, r) +} + +func (b *Reader) reset(buf []byte, r io.Reader) { + *b = Reader{ + buf: buf, + rd: r, + lastByte: -1, + lastRuneSize: -1, + } +} + var errNegativeRead = errors.New("bufio: reader returned negative count from Read") // fill reads a new chunk into the buffer. @@ -234,7 +246,7 @@ func (b *Reader) Buffered() int { return b.w - b.r } // ReadSlice reads until the first occurrence of delim in the input, // returning a slice pointing at the bytes in the buffer. -// The bytes stop being valid at the next read call. +// The bytes stop being valid at the next read. // If ReadSlice encounters an error before finding a delimiter, // it returns all the data in the buffer and the error itself (often io.EOF). // ReadSlice fails with error ErrBufferFull if the buffer fills without a delim. @@ -381,7 +393,8 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err error) { // For simple uses, a Scanner may be more convenient. func (b *Reader) ReadString(delim byte) (line string, err error) { bytes, err := b.ReadBytes(delim) - return string(bytes), err + line = string(bytes) + return line, err } // WriteTo implements io.WriterTo. @@ -424,6 +437,9 @@ func (b *Reader) writeBuf(w io.Writer) (int64, error) { // Writer implements buffering for an io.Writer object. // If an error occurs writing to a Writer, no more data will be // accepted and all subsequent writes will return the error. +// After all data has been written, the client should call the +// Flush method to guarantee all data has been forwarded to +// the underlying io.Writer. type Writer struct { err error buf []byte @@ -434,28 +450,41 @@ type Writer struct { // NewWriterSize returns a new Writer whose buffer has at least the specified // size. If the argument io.Writer is already a Writer with large enough // size, it returns the underlying Writer. -func NewWriterSize(wr io.Writer, size int) *Writer { +func NewWriterSize(w io.Writer, size int) *Writer { // Is it already a Writer? - b, ok := wr.(*Writer) + b, ok := w.(*Writer) if ok && len(b.buf) >= size { return b } if size <= 0 { size = defaultBufSize } - b = new(Writer) - b.buf = make([]byte, size) - b.wr = wr - return b + return &Writer{ + buf: make([]byte, size), + wr: w, + } } // NewWriter returns a new Writer whose buffer has the default size. -func NewWriter(wr io.Writer) *Writer { - return NewWriterSize(wr, defaultBufSize) +func NewWriter(w io.Writer) *Writer { + return NewWriterSize(w, defaultBufSize) +} + +// Reset discards any unflushed buffered data, clears any error, and +// resets b to write its output to w. +func (b *Writer) Reset(w io.Writer) { + b.err = nil + b.n = 0 + b.wr = w } // Flush writes any buffered data to the underlying io.Writer. func (b *Writer) Flush() error { + err := b.flush() + return err +} + +func (b *Writer) flush() error { if b.err != nil { return b.err } @@ -498,7 +527,7 @@ func (b *Writer) Write(p []byte) (nn int, err error) { } else { n = copy(b.buf[b.n:], p) b.n += n - b.Flush() + b.flush() } nn += n p = p[n:] @@ -517,7 +546,7 @@ func (b *Writer) WriteByte(c byte) error { if b.err != nil { return b.err } - if b.Available() <= 0 && b.Flush() != nil { + if b.Available() <= 0 && b.flush() != nil { return b.err } b.buf[b.n] = c @@ -540,7 +569,7 @@ func (b *Writer) WriteRune(r rune) (size int, err error) { } n := b.Available() if n < utf8.UTFMax { - if b.Flush(); b.err != nil { + if b.flush(); b.err != nil { return 0, b.err } n = b.Available() @@ -565,7 +594,7 @@ func (b *Writer) WriteString(s string) (int, error) { b.n += n nn += n s = s[n:] - b.Flush() + b.flush() } if b.err != nil { return nn, b.err @@ -585,23 +614,28 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { } var m int for { + if b.Available() == 0 { + if err1 := b.flush(); err1 != nil { + return n, err1 + } + } m, err = r.Read(b.buf[b.n:]) if m == 0 { break } b.n += m n += int64(m) - if b.Available() == 0 { - if err1 := b.Flush(); err1 != nil { - return n, err1 - } - } if err != nil { break } } if err == io.EOF { - err = nil + // If we filled the buffer exactly, flush pre-emptively. + if b.Available() == 0 { + err = b.flush() + } else { + err = nil + } } return n, err } diff --git a/libgo/go/bufio/bufio_test.go b/libgo/go/bufio/bufio_test.go index 79ed0f1..41bd3d45 100644 --- a/libgo/go/bufio/bufio_test.go +++ b/libgo/go/bufio/bufio_test.go @@ -847,6 +847,10 @@ func TestWriterReadFrom(t *testing.T) { t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input)) continue } + if err := w.Flush(); err != nil { + t.Errorf("Flush returned %v", err) + continue + } if got, want := b.String(), string(input); got != want { t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want) } @@ -1003,6 +1007,56 @@ func TestReaderClearError(t *testing.T) { } } +// Test for golang.org/issue/5947 +func TestWriterReadFromWhileFull(t *testing.T) { + buf := new(bytes.Buffer) + w := NewWriterSize(buf, 10) + + // Fill buffer exactly. + n, err := w.Write([]byte("0123456789")) + if n != 10 || err != nil { + t.Fatalf("Write returned (%v, %v), want (10, nil)", n, err) + } + + // 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) + } +} + +func TestReaderReset(t *testing.T) { + r := NewReader(strings.NewReader("foo foo")) + buf := make([]byte, 3) + r.Read(buf) + if string(buf) != "foo" { + t.Errorf("buf = %q; want foo", buf) + } + r.Reset(strings.NewReader("bar bar")) + all, err := ioutil.ReadAll(r) + if err != nil { + t.Fatal(err) + } + if string(all) != "bar bar" { + t.Errorf("ReadAll = %q; want bar bar", all) + } +} + +func TestWriterReset(t *testing.T) { + var buf1, buf2 bytes.Buffer + w := NewWriter(&buf1) + w.WriteString("foo") + w.Reset(&buf2) // and not flushed + w.WriteString("bar") + w.Flush() + if buf1.String() != "" { + t.Errorf("buf1 = %q; want empty", buf1.String()) + } + if buf2.String() != "bar" { + t.Errorf("buf2 = %q; want bar", buf2.String()) + } +} + // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have. type onlyReader struct { r io.Reader @@ -1083,3 +1137,46 @@ func BenchmarkWriterCopyNoReadFrom(b *testing.B) { io.Copy(dst, src) } } + +func BenchmarkReaderEmpty(b *testing.B) { + b.ReportAllocs() + str := strings.Repeat("x", 16<<10) + for i := 0; i < b.N; i++ { + br := NewReader(strings.NewReader(str)) + n, err := io.Copy(ioutil.Discard, br) + if err != nil { + b.Fatal(err) + } + if n != int64(len(str)) { + b.Fatal("wrong length") + } + } +} + +func BenchmarkWriterEmpty(b *testing.B) { + b.ReportAllocs() + str := strings.Repeat("x", 1<<10) + bs := []byte(str) + for i := 0; i < b.N; i++ { + bw := NewWriter(ioutil.Discard) + bw.Flush() + bw.WriteByte('a') + bw.Flush() + bw.WriteRune('B') + bw.Flush() + bw.Write(bs) + bw.Flush() + bw.WriteString(str) + bw.Flush() + } +} + +func BenchmarkWriterFlush(b *testing.B) { + b.ReportAllocs() + bw := NewWriter(ioutil.Discard) + str := strings.Repeat("x", 50) + for i := 0; i < b.N; i++ { + bw.WriteString(str) + bw.Flush() + } +} diff --git a/libgo/go/bufio/example_test.go b/libgo/go/bufio/example_test.go index 08a3944..3da9141 100644 --- a/libgo/go/bufio/example_test.go +++ b/libgo/go/bufio/example_test.go @@ -12,6 +12,14 @@ import ( "strings" ) +func ExampleWriter() { + w := bufio.NewWriter(os.Stdout) + fmt.Fprint(w, "Hello, ") + fmt.Fprint(w, "world!") + w.Flush() // Don't forget to flush! + // Output: Hello, world! +} + // The simplest use of a Scanner, to read standard input as a set of lines. func ExampleScanner_lines() { scanner := bufio.NewScanner(os.Stdin) diff --git a/libgo/go/bufio/scan.go b/libgo/go/bufio/scan.go index 2e1a2e9..423505f 100644 --- a/libgo/go/bufio/scan.go +++ b/libgo/go/bufio/scan.go @@ -44,8 +44,8 @@ type Scanner struct { // to give. The return values are the number of bytes to advance the input // and the next token to return to the user, plus an error, if any. If the // data does not yet hold a complete token, for instance if it has no newline -// while scanning lines, SplitFunc can return (0, nil) to signal the Scanner -// to read more data into the slice and try again with a longer slice +// while scanning lines, SplitFunc can return (0, nil, nil) to signal the +// Scanner to read more data into the slice and try again with a longer slice // starting at the same point in the input. // // If the returned error is non-nil, scanning stops and the error @@ -287,7 +287,7 @@ func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) { return 0, nil, nil } -// isSpace returns whether the character is a Unicode white space character. +// isSpace reports whether the character is a Unicode white space character. // We avoid dependency on the unicode package, but check validity of the implementation // in the tests. func isSpace(r rune) bool { diff --git a/libgo/go/builtin/builtin.go b/libgo/go/builtin/builtin.go index 128a1b5..51550a4 100644 --- a/libgo/go/builtin/builtin.go +++ b/libgo/go/builtin/builtin.go @@ -236,6 +236,19 @@ func panic(v interface{}) // panicking. func recover() interface{} +// The print built-in function formats its arguments in an implementation- +// specific way and writes the result to standard error. +// Print is useful for bootstrapping and debugging; it is not guaranteed +// to stay in the language. +func print(args ...Type) + +// The println built-in function formats its arguments in an implementation- +// specific way and writes the result to standard error. +// Spaces are always added between arguments and a newline is appended. +// Println is useful for bootstrapping and debugging; it is not guaranteed +// to stay in the language. +func println(args ...Type) + // The error built-in interface type is the conventional interface for // representing an error condition, with the nil value representing no error. type error interface { diff --git a/libgo/go/bytes/bytes.go b/libgo/go/bytes/bytes.go index e42f744..01a5d9ae 100644 --- a/libgo/go/bytes/bytes.go +++ b/libgo/go/bytes/bytes.go @@ -11,32 +11,6 @@ import ( "unicode/utf8" ) -// Compare returns an integer comparing two byte slices lexicographically. -// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. -// A nil argument is equivalent to an empty slice. -func Compare(a, b []byte) int { - m := len(a) - if m > len(b) { - m = len(b) - } - for i, ac := range a[0:m] { - bc := b[i] - switch { - case ac > bc: - return 1 - case ac < bc: - return -1 - } - } - switch { - case len(a) < len(b): - return -1 - case len(a) > len(b): - return 1 - } - return 0 -} - func equalPortable(a, b []byte) bool { if len(a) != len(b) { return false @@ -103,7 +77,7 @@ func Count(s, sep []byte) int { return count } -// Contains returns whether subslice is within b. +// Contains reports whether subslice is within b. func Contains(b, subslice []byte) bool { return Index(b, subslice) != -1 } @@ -401,10 +375,7 @@ func Repeat(b []byte, count int) []byte { nb := make([]byte, len(b)*count) bp := 0 for i := 0; i < count; i++ { - for j := 0; j < len(b); j++ { - nb[bp] = b[j] - bp++ - } + bp += copy(nb[bp:], b) } return nb } diff --git a/libgo/go/bytes/bytes_decl.go b/libgo/go/bytes/bytes_decl.go index fbf9282..617d748 100644 --- a/libgo/go/bytes/bytes_decl.go +++ b/libgo/go/bytes/bytes_decl.go @@ -7,10 +7,18 @@ package bytes //go:noescape // IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s. -func IndexByte(s []byte, c byte) int // asm_$GOARCH.s +func IndexByte(s []byte, c byte) int // ../runtime/asm_$GOARCH.s //go:noescape -// Equal returns a boolean reporting whether a == b. +// Equal returns a boolean reporting whether a and b +// are the same length and contain the same bytes. // A nil argument is equivalent to an empty slice. -func Equal(a, b []byte) bool // asm_arm.s or ../runtime/asm_{386,amd64}.s +func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s + +//go:noescape + +// Compare returns an integer comparing two byte slices lexicographically. +// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. +// A nil argument is equivalent to an empty slice. +func Compare(a, b []byte) int // ../runtime/noasm_arm.goc or ../runtime/asm_{386,amd64}.s diff --git a/libgo/go/bytes/bytes_test.go b/libgo/go/bytes/bytes_test.go index d296224..ab5da4f 100644 --- a/libgo/go/bytes/bytes_test.go +++ b/libgo/go/bytes/bytes_test.go @@ -47,7 +47,7 @@ type BinOpTest struct { i int } -var compareTests = []struct { +var equalTests = []struct { a, b []byte i int }{ @@ -73,12 +73,8 @@ var compareTests = []struct { {nil, []byte("a"), -1}, } -func TestCompare(t *testing.T) { +func TestEqual(t *testing.T) { for _, tt := range compareTests { - cmp := Compare(tt.a, tt.b) - if cmp != tt.i { - t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp) - } eql := Equal(tt.a, tt.b) if eql != (tt.i == 0) { t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql) @@ -90,7 +86,7 @@ func TestCompare(t *testing.T) { } } -func TestEqual(t *testing.T) { +func TestEqualExhaustive(t *testing.T) { var size = 128 if testing.Short() { size = 32 @@ -147,6 +143,7 @@ var indexTests = []BinOpTest{ {"", "a", -1}, {"", "foo", -1}, {"fo", "foo", -1}, + {"foo", "baz", -1}, {"foo", "foo", 0}, {"oofofoofooo", "f", 2}, {"oofofoofooo", "foo", 4}, @@ -1086,6 +1083,24 @@ func TestTitle(t *testing.T) { } } +var ToTitleTests = []TitleTest{ + {"", ""}, + {"a", "A"}, + {" aaa aaa aaa ", " AAA AAA AAA "}, + {" Aaa Aaa Aaa ", " AAA AAA AAA "}, + {"123a456", "123A456"}, + {"double-blind", "DOUBLE-BLIND"}, + {"ÿøû", "ŸØÛ"}, +} + +func TestToTitle(t *testing.T) { + for _, tt := range ToTitleTests { + if s := string(ToTitle([]byte(tt.in))); s != tt.out { + t.Errorf("ToTitle(%q) = %q, want %q", tt.in, s, tt.out) + } + } +} + var EqualFoldTests = []struct { s, t string out bool @@ -1114,6 +1129,37 @@ func TestEqualFold(t *testing.T) { } } +func TestBufferGrowNegative(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Fatal("Grow(-1) should have paniced") + } + }() + var b Buffer + b.Grow(-1) +} + +func TestBufferTruncateNegative(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Fatal("Truncate(-1) should have paniced") + } + }() + var b Buffer + b.Truncate(-1) +} + +func TestBufferTruncateOutOfRange(t *testing.T) { + defer func() { + if err := recover(); err == nil { + t.Fatal("Truncate(20) should have paniced") + } + }() + var b Buffer + b.Write(make([]byte, 10)) + b.Truncate(20) +} + var makeFieldsInput = func() []byte { x := make([]byte, 1<<20) // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. diff --git a/libgo/go/bytes/compare_test.go b/libgo/go/bytes/compare_test.go new file mode 100644 index 0000000..0a36f5a --- /dev/null +++ b/libgo/go/bytes/compare_test.go @@ -0,0 +1,204 @@ +package bytes_test + +import ( + . "bytes" + "testing" +) + +var compareTests = []struct { + a, b []byte + i int +}{ + {[]byte(""), []byte(""), 0}, + {[]byte("a"), []byte(""), 1}, + {[]byte(""), []byte("a"), -1}, + {[]byte("abc"), []byte("abc"), 0}, + {[]byte("ab"), []byte("abc"), -1}, + {[]byte("abc"), []byte("ab"), 1}, + {[]byte("x"), []byte("ab"), 1}, + {[]byte("ab"), []byte("x"), -1}, + {[]byte("x"), []byte("a"), 1}, + {[]byte("b"), []byte("x"), -1}, + // test runtime·memeq's chunked implementation + {[]byte("abcdefgh"), []byte("abcdefgh"), 0}, + {[]byte("abcdefghi"), []byte("abcdefghi"), 0}, + {[]byte("abcdefghi"), []byte("abcdefghj"), -1}, + // nil tests + {nil, nil, 0}, + {[]byte(""), nil, 0}, + {nil, []byte(""), 0}, + {[]byte("a"), nil, 1}, + {nil, []byte("a"), -1}, +} + +func TestCompare(t *testing.T) { + for _, tt := range compareTests { + cmp := Compare(tt.a, tt.b) + if cmp != tt.i { + t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp) + } + } +} + +func TestCompareIdenticalSlice(t *testing.T) { + var b = []byte("Hello Gophers!") + if Compare(b, b) != 0 { + t.Error("b != b") + } + if Compare(b, b[:1]) != 1 { + t.Error("b > b[:1] failed") + } +} + +func TestCompareBytes(t *testing.T) { + n := 128 + a := make([]byte, n+1) + b := make([]byte, n+1) + for len := 0; len < 128; len++ { + // randomish but deterministic data. No 0 or 255. + for i := 0; i < len; i++ { + a[i] = byte(1 + 31*i%254) + b[i] = byte(1 + 31*i%254) + } + // data past the end is different + for i := len; i <= n; i++ { + a[i] = 8 + b[i] = 9 + } + cmp := Compare(a[:len], b[:len]) + if cmp != 0 { + t.Errorf(`CompareIdentical(%d) = %d`, len, cmp) + } + if len > 0 { + cmp = Compare(a[:len-1], b[:len]) + if cmp != -1 { + t.Errorf(`CompareAshorter(%d) = %d`, len, cmp) + } + cmp = Compare(a[:len], b[:len-1]) + if cmp != 1 { + t.Errorf(`CompareBshorter(%d) = %d`, len, cmp) + } + } + for k := 0; k < len; k++ { + b[k] = a[k] - 1 + cmp = Compare(a[:len], b[:len]) + if cmp != 1 { + t.Errorf(`CompareAbigger(%d,%d) = %d`, len, k, cmp) + } + b[k] = a[k] + 1 + cmp = Compare(a[:len], b[:len]) + if cmp != -1 { + t.Errorf(`CompareBbigger(%d,%d) = %d`, len, k, cmp) + } + b[k] = a[k] + } + } +} + +func BenchmarkCompareBytesEqual(b *testing.B) { + b1 := []byte("Hello Gophers!") + b2 := []byte("Hello Gophers!") + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != 0 { + b.Fatal("b1 != b2") + } + } +} + +func BenchmarkCompareBytesToNil(b *testing.B) { + b1 := []byte("Hello Gophers!") + var b2 []byte + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != 1 { + b.Fatal("b1 > b2 failed") + } + } +} + +func BenchmarkCompareBytesEmpty(b *testing.B) { + b1 := []byte("") + b2 := b1 + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != 0 { + b.Fatal("b1 != b2") + } + } +} + +func BenchmarkCompareBytesIdentical(b *testing.B) { + b1 := []byte("Hello Gophers!") + b2 := b1 + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != 0 { + b.Fatal("b1 != b2") + } + } +} + +func BenchmarkCompareBytesSameLength(b *testing.B) { + b1 := []byte("Hello Gophers!") + b2 := []byte("Hello, Gophers") + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != -1 { + b.Fatal("b1 < b2 failed") + } + } +} + +func BenchmarkCompareBytesDifferentLength(b *testing.B) { + b1 := []byte("Hello Gophers!") + b2 := []byte("Hello, Gophers!") + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != -1 { + b.Fatal("b1 < b2 failed") + } + } +} + +func BenchmarkCompareBytesBigUnaligned(b *testing.B) { + b.StopTimer() + b1 := make([]byte, 0, 1<<20) + for len(b1) < 1<<20 { + b1 = append(b1, "Hello Gophers!"...) + } + b2 := append([]byte("hello"), b1...) + b.StartTimer() + for i := 0; i < b.N; i++ { + if Compare(b1, b2[len("hello"):]) != 0 { + b.Fatal("b1 != b2") + } + } + b.SetBytes(int64(len(b1))) +} + +func BenchmarkCompareBytesBig(b *testing.B) { + b.StopTimer() + b1 := make([]byte, 0, 1<<20) + for len(b1) < 1<<20 { + b1 = append(b1, "Hello Gophers!"...) + } + b2 := append([]byte{}, b1...) + b.StartTimer() + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != 0 { + b.Fatal("b1 != b2") + } + } + b.SetBytes(int64(len(b1))) +} + +func BenchmarkCompareBytesBigIdentical(b *testing.B) { + b.StopTimer() + b1 := make([]byte, 0, 1<<20) + for len(b1) < 1<<20 { + b1 = append(b1, "Hello Gophers!"...) + } + b2 := b1 + b.StartTimer() + for i := 0; i < b.N; i++ { + if Compare(b1, b2) != 0 { + b.Fatal("b1 != b2") + } + } + b.SetBytes(int64(len(b1))) +} diff --git a/libgo/go/bytes/indexbyte.c b/libgo/go/bytes/indexbyte.c index cbc7847..b248108 100644 --- a/libgo/go/bytes/indexbyte.c +++ b/libgo/go/bytes/indexbyte.c @@ -41,3 +41,33 @@ Equal (struct __go_open_array a, struct __go_open_array b) return 0; return __builtin_memcmp (a.__values, b.__values, a.__count) == 0; } + +intgo Compare (struct __go_open_array a, struct __go_open_array b) + __asm__ (GOSYM_PREFIX "bytes.Compare") + __attribute__ ((no_split_stack)); + +intgo +Compare (struct __go_open_array a, struct __go_open_array b) +{ + intgo len; + + len = a.__count; + if (len > b.__count) + len = b.__count; + if (len > 0) + { + intgo ret; + + ret = __builtin_memcmp (a.__values, b.__values, len); + if (ret < 0) + return -1; + else if (ret > 0) + return 1; + } + if (a.__count < b.__count) + return -1; + else if (a.__count > b.__count) + return 1; + else + return 0; +} diff --git a/libgo/go/bytes/reader_test.go b/libgo/go/bytes/reader_test.go index f0a3e26..19f014d 100644 --- a/libgo/go/bytes/reader_test.go +++ b/libgo/go/bytes/reader_test.go @@ -113,6 +113,41 @@ func TestReaderWriteTo(t *testing.T) { } } +func TestReaderLen(t *testing.T) { + const data = "hello world" + r := NewReader([]byte(data)) + if got, want := r.Len(), 11; got != want { + t.Errorf("r.Len(): got %d, want %d", got, want) + } + if n, err := r.Read(make([]byte, 10)); err != nil || n != 10 { + t.Errorf("Read failed: read %d %v", n, err) + } + if got, want := r.Len(), 1; got != want { + t.Errorf("r.Len(): got %d, want %d", got, want) + } + if n, err := r.Read(make([]byte, 1)); err != nil || n != 1 { + t.Errorf("Read failed: read %d %v", n, err) + } + if got, want := r.Len(), 0; got != want { + t.Errorf("r.Len(): got %d, want %d", got, want) + } +} + +func TestReaderDoubleUnreadRune(t *testing.T) { + buf := NewBuffer([]byte("groucho")) + if _, _, err := buf.ReadRune(); err != nil { + // should not happen + t.Fatal(err) + } + if err := buf.UnreadByte(); err != nil { + // should not happen + t.Fatal(err) + } + if err := buf.UnreadByte(); err == nil { + t.Fatal("UnreadByte: expected error, got nil") + } +} + // verify that copying from an empty reader always has the same results, // regardless of the presence of a WriteTo method. func TestReaderCopyNothing(t *testing.T) { diff --git a/libgo/go/compress/bzip2/bit_reader.go b/libgo/go/compress/bzip2/bit_reader.go index ab1d606..32d1036 100644 --- a/libgo/go/compress/bzip2/bit_reader.go +++ b/libgo/go/compress/bzip2/bit_reader.go @@ -77,6 +77,14 @@ func (br *bitReader) ReadBit() bool { return n != 0 } +func (br *bitReader) TryReadBit() (bit byte, ok bool) { + if br.bits > 0 { + br.bits-- + return byte(br.n>>br.bits) & 1, true + } + return 0, false +} + func (br *bitReader) Err() error { return br.err } diff --git a/libgo/go/compress/bzip2/bzip2.go b/libgo/go/compress/bzip2/bzip2.go index 3dc8c62..82e30c7 100644 --- a/libgo/go/compress/bzip2/bzip2.go +++ b/libgo/go/compress/bzip2/bzip2.go @@ -22,14 +22,17 @@ func (s StructuralError) Error() string { // A reader decompresses bzip2 compressed data. type reader struct { - br bitReader - setupDone bool // true if we have parsed the bzip2 header. - blockSize int // blockSize in bytes, i.e. 900 * 1024. - eof bool - buf []byte // stores Burrows-Wheeler transformed data. - c [256]uint // the `C' array for the inverse BWT. - tt []uint32 // mirrors the `tt' array in the bzip2 source and contains the P array in the upper 24 bits. - tPos uint32 // Index of the next output byte in tt. + br bitReader + fileCRC uint32 + blockCRC uint32 + wantBlockCRC uint32 + setupDone bool // true if we have parsed the bzip2 header. + blockSize int // blockSize in bytes, i.e. 900 * 1024. + eof bool + buf []byte // stores Burrows-Wheeler transformed data. + c [256]uint // the `C' array for the inverse BWT. + tt []uint32 // mirrors the `tt' array in the bzip2 source and contains the P array in the upper 24 bits. + tPos uint32 // Index of the next output byte in tt. preRLE []uint32 // contains the RLE data still to be processed. preRLEUsed int // number of entries of preRLE used. @@ -50,12 +53,14 @@ const bzip2BlockMagic = 0x314159265359 const bzip2FinalMagic = 0x177245385090 // setup parses the bzip2 header. -func (bz2 *reader) setup() error { +func (bz2 *reader) setup(needMagic bool) error { br := &bz2.br - magic := br.ReadBits(16) - if magic != bzip2FileMagic { - return StructuralError("bad magic value") + if needMagic { + magic := br.ReadBits(16) + if magic != bzip2FileMagic { + return StructuralError("bad magic value") + } } t := br.ReadBits(8) @@ -68,8 +73,11 @@ func (bz2 *reader) setup() error { return StructuralError("invalid compression level") } + bz2.fileCRC = 0 bz2.blockSize = 100 * 1024 * (int(level) - '0') - bz2.tt = make([]uint32, bz2.blockSize) + if bz2.blockSize > len(bz2.tt) { + bz2.tt = make([]uint32, bz2.blockSize) + } return nil } @@ -79,7 +87,7 @@ func (bz2 *reader) Read(buf []byte) (n int, err error) { } if !bz2.setupDone { - err = bz2.setup() + err = bz2.setup(true) brErr := bz2.br.Err() if brErr != nil { err = brErr @@ -98,14 +106,14 @@ func (bz2 *reader) Read(buf []byte) (n int, err error) { return } -func (bz2 *reader) read(buf []byte) (n int, err error) { +func (bz2 *reader) readFromBlock(buf []byte) int { // bzip2 is a block based compressor, except that it has a run-length // preprocessing step. The block based nature means that we can // preallocate fixed-size buffers and reuse them. However, the RLE // preprocessing would require allocating huge buffers to store the // maximum expansion. Thus we process blocks all at once, except for // the RLE which we decompress as required. - + n := 0 for (bz2.repeats > 0 || bz2.preRLEUsed < len(bz2.preRLE)) && n < len(buf) { // We have RLE data pending. @@ -148,34 +156,87 @@ func (bz2 *reader) read(buf []byte) (n int, err error) { n++ } - if n > 0 { - return - } + return n +} - // No RLE data is pending so we need to read a block. +func (bz2 *reader) read(buf []byte) (int, error) { + for { + n := bz2.readFromBlock(buf) + if n > 0 { + bz2.blockCRC = updateCRC(bz2.blockCRC, buf[:n]) + return n, nil + } - br := &bz2.br - magic := br.ReadBits64(48) - if magic == bzip2FinalMagic { - br.ReadBits64(32) // ignored CRC - bz2.eof = true - return 0, io.EOF - } else if magic != bzip2BlockMagic { - return 0, StructuralError("bad magic value found") - } + // End of block. Check CRC. + if bz2.blockCRC != bz2.wantBlockCRC { + bz2.br.err = StructuralError("block checksum mismatch") + return 0, bz2.br.err + } - err = bz2.readBlock() - if err != nil { - return 0, err - } + // Find next block. + br := &bz2.br + switch br.ReadBits64(48) { + default: + return 0, StructuralError("bad magic value found") + + case bzip2BlockMagic: + // Start of block. + err := bz2.readBlock() + if err != nil { + return 0, err + } - return bz2.read(buf) + case bzip2FinalMagic: + // Check end-of-file CRC. + wantFileCRC := uint32(br.ReadBits64(32)) + if br.err != nil { + return 0, br.err + } + if bz2.fileCRC != wantFileCRC { + br.err = StructuralError("file checksum mismatch") + return 0, br.err + } + + // Skip ahead to byte boundary. + // Is there a file concatenated to this one? + // It would start with BZ. + if br.bits%8 != 0 { + br.ReadBits(br.bits % 8) + } + b, err := br.r.ReadByte() + if err == io.EOF { + br.err = io.EOF + bz2.eof = true + return 0, io.EOF + } + if err != nil { + br.err = err + return 0, err + } + z, err := br.r.ReadByte() + if err != nil { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + br.err = err + return 0, err + } + if b != 'B' || z != 'Z' { + return 0, StructuralError("bad magic value in continuation file") + } + if err := bz2.setup(false); err != nil { + return 0, err + } + } + } } // readBlock reads a bzip2 block. The magic number should already have been consumed. func (bz2 *reader) readBlock() (err error) { br := &bz2.br - br.ReadBits64(32) // skip checksum. TODO: check it if we can figure out what it is. + bz2.wantBlockCRC = uint32(br.ReadBits64(32)) // skip checksum. TODO: check it if we can figure out what it is. + bz2.blockCRC = 0 + bz2.fileCRC = (bz2.fileCRC<<1 | bz2.fileCRC>>31) ^ bz2.wantBlockCRC randomized := br.ReadBits(1) if randomized != 0 { return StructuralError("deprecated randomized files") @@ -316,6 +377,9 @@ func (bz2 *reader) readBlock() (err error) { if repeat > 0 { // We have decoded a complete run-length so we need to // replicate the last output symbol. + if repeat > bz2.blockSize-bufIndex { + return StructuralError("repeats past end of block") + } for i := 0; i < repeat; i++ { b := byte(mtf.First()) bz2.tt[bufIndex] = uint32(b) @@ -339,6 +403,9 @@ func (bz2 *reader) readBlock() (err error) { // doesn't need to be encoded and we have |v-1| in the next // line. b := byte(mtf.Decode(int(v - 1))) + if bufIndex >= bz2.blockSize { + return StructuralError("data exceeds block size") + } bz2.tt[bufIndex] = uint32(b) bz2.c[b]++ bufIndex++ @@ -385,3 +452,33 @@ func inverseBWT(tt []uint32, origPtr uint, c []uint) uint32 { return tt[origPtr] >> 8 } + +// This is a standard CRC32 like in hash/crc32 except that all the shifts are reversed, +// causing the bits in the input to be processed in the reverse of the usual order. + +var crctab [256]uint32 + +func init() { + const poly = 0x04C11DB7 + for i := range crctab { + crc := uint32(i) << 24 + for j := 0; j < 8; j++ { + if crc&0x80000000 != 0 { + crc = (crc << 1) ^ poly + } else { + crc <<= 1 + } + } + crctab[i] = crc + } +} + +// updateCRC updates the crc value to incorporate the data in b. +// The initial value is 0. +func updateCRC(val uint32, b []byte) uint32 { + crc := ^val + for _, v := range b { + crc = crctab[byte(crc>>24)^v] ^ (crc << 8) + } + return ^crc +} diff --git a/libgo/go/compress/bzip2/bzip2_test.go b/libgo/go/compress/bzip2/bzip2_test.go index 7b227ac..ada1f9a 100644 --- a/libgo/go/compress/bzip2/bzip2_test.go +++ b/libgo/go/compress/bzip2/bzip2_test.go @@ -6,6 +6,7 @@ package bzip2 import ( "bytes" + "encoding/base64" "encoding/hex" "io" "io/ioutil" @@ -62,6 +63,19 @@ func TestHelloWorldBZ2(t *testing.T) { } } +func TestConcat(t *testing.T) { + out, err := decompressHex(helloWorldBZ2Hex + helloWorldBZ2Hex) + if err != nil { + t.Errorf("error from Read: %s", err) + return + } + + hello2 := bytes.Repeat(helloWorld, 2) + if !bytes.Equal(hello2, out) { + t.Errorf("got %x, want %x", out, hello2) + } +} + func testZeros(t *testing.T, inHex string, n int) { out, err := decompressHex(inHex) if err != nil { @@ -155,3 +169,195 @@ const rand2Hex = "92d5652616ac444a4a04af1a8a3964aca0450d43d6cf233bd03233f4ba92f8 const rand3BZ2Hex = "425a68393141592653593be669d00000327ffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffc002b3b2b1b6e2bae400004c00132300004c0d268c004c08c0130026001a008683234c0684c34008c230261a04c0260064d07a8d00034000d27a1268c9931a8d327a3427a41faa69ea0da264c1a34219326869b51b49a6469a3268c689fa53269a62794687a9a68f5189994c9e487a8f534fd49a3d34043629e8c93d04da4f4648d30d4f44d3234c4d3023d0840680984d309934c234d3131a000640984f536a6132601300130130c8d00d04d1841ea7a8d31a02609b40023460010c01a34d4c1a0d04d3069306810034d0d0d4c0046130d034d0131a9a64d321804c68003400098344c13000991808c0001a00000000098004d3d4da4604c47a13012140aadf8d673c922c607ef6212a8c0403adea4b28aee578900e653b9cdeb8d11e6b838815f3ebaad5a01c5408d84a332170aff8734d4e06612d3c2889f31925fb89e33561f5100ae89b1f7047102e729373d3667e58d73aaa80fa7be368a1cc2dadd81d81ec8e1b504bd772ca31d03649269b01ceddaca07bf3d4eba24de141be3f86f93601e03714c0f64654671684f9f9528626fd4e1b76753dc0c54b842486b8d59d8ab314e86ca818e7a1f079463cbbd70d9b79b283c7edc419406311022e4be98c2c1374df9cdde2d008ce1d00e5f06ad1024baf555631f70831fc1023034e62be7c4bcb648caf276963ffa20e96bb50377fe1c113da0db4625b50741c35a058edb009c6ee5dbf93b8a6b060eec568180e8db791b82aab96cbf4326ca98361461379425ba8dcc347be670bdba7641883e5526ae3d833f6e9cb9bac9557747c79e206151072f7f0071dff3880411846f66bf4075c7462f302b53cb3400a74cf35652ad5641ed33572fd54e7ed7f85f58a0acba89327e7c6be5c58cb71528b99df2431f1d0358f8d28d81d95292da631fb06701decabb205fac59ff0fb1df536afc681eece6ea658c4d9eaa45f1342aa1ff70bdaff2ddaf25ec88c22f12829a0553db1ec2505554cb17d7b282e213a5a2aa30431ded2bce665bb199d023840832fedb2c0c350a27291407ff77440792872137df281592e82076a05c64c345ffb058c64f7f7c207ef78420b7010520610f17e302cc4dfcfaef72a0ed091aab4b541eb0531bbe941ca2f792bf7b31ca6162882b68054a8470115bc2c19f2df2023f7800432b39b04d3a304e8085ba3f1f0ca5b1ba4d38d339e6084de979cdea6d0e244c6c9fa0366bd890621e3d30846f5e8497e21597b8f29bbf52c961a485dfbea647600da0fc1f25ce4d203a8352ece310c39073525044e7ac46acf2ed9120bae1b4f6f02364abfe343f80b290983160c103557af1c68416480d024cc31b6c06cfec011456f1e95c420a12b48b1c3fe220c2879a982fb099948ac440db844b9a112a5188c7783fd3b19593290785f908d95c9db4b280bafe89c1313aeec24772046d9bc089645f0d182a21184e143823c5f52de50e5d7e98d3d7ab56f5413bbccd1415c9bcff707def475b643fb7f29842582104d4cc1dbaaca8f10a2f44273c339e0984f2b1e06ab2f0771db01fafa8142298345f3196f23e5847bda024034b6f59b11c29e981c881456e40d211929fd4f766200258aad8212016322bd5c605790dcfdf1bd2a93d99c9b8f498722d311d7eae7ff420496a31804c55f4759a7b13aaaf5f7ce006c3a8a998897d5e0a504398c2b627852545baf440798bcc5cc049357cf3f17d9771e4528a1af3d77dc794a11346e1bdf5efe37a405b127b4c43b616d61fbc5dc914e14240ef99a7400" const rand3Hex = "1744b384d68c042371244e13500d4bfb98c6244e3d71a5b700224420b59c593553f33bd786e3d0ce31626f511bc985f59d1a88aa38ba8ad6218d306abee60dd9172540232b95be1af146c69e72e5fde667a090dc3f93bdc5c5af0ab80acdbaa7a505f628c59dc0247b31a439cacf5010a94376d71521df08c178b02fb96fdb1809144ea38c68536187c53201fea8631fb0a880b4451ccdca7cc61f6aafca21cc7449d920599db61789ac3b1e164b3390124f95022aeea39ccca3ec1053f4fa10de2978e2861ea58e477085c2220021a0927aa94c5d0006b5055abba340e4f9eba22e969978dfd18e278a8b89d877328ae34268bc0174cfe211954c0036f078025217d1269fac1932a03b05a0b616012271bbe1fb554171c7a59b196d8a4479f45a77931b5d97aaf6c0c673cbe597b79b96e2a0c1eae2e66e46ccc8c85798e23ffe972ebdaa3f6caea243c004e60321eb47cd79137d78fd0613be606feacc5b3637bdc96a89c13746db8cad886f3ccf912b2178c823bcac395f06d28080269bdca2debf3419c66c690fd1adcfbd53e32e79443d7a42511a84cb22ca94fffad9149275a075b2f8ae0b021dcde9bf62b102db920733b897560518b06e1ad7f4b03458493ddaa7f4fa2c1609f7a1735aeeb1b3e2cea3ab45fc376323cc91873b7e9c90d07c192e38d3f5dfc9bfab1fd821c854da9e607ea596c391c7ec4161c6c4493929a8176badaa5a5af7211c623f29643a937677d3df0da9266181b7c4da5dd40376db677fe8f4a1dc456adf6f33c1e37cec471dd318c2647644fe52f93707a77da7d1702380a80e14cc0fdce7bf2eed48a529090bae0388ee277ce6c7018c5fb00b88362554362205c641f0d0fab94fd5b8357b5ff08b207fee023709bc126ec90cfb17c006754638f8186aaeb1265e80be0c1189ec07d01d5f6f96cb9ce82744147d18490de7dc72862f42f024a16968891a356f5e7e0e695d8c933ba5b5e43ad4c4ade5399bc2cae9bb6189b7870d7f22956194d277f28b10e01c10c6ffe3e065f7e2d6d056aa790db5649ca84dc64c35566c0af1b68c32b5b7874aaa66467afa44f40e9a0846a07ae75360a641dd2acc69d93219b2891f190621511e62a27f5e4fbe641ece1fa234fc7e9a74f48d2a760d82160d9540f649256b169d1fed6fbefdc491126530f3cbad7913e19fbd7aa53b1e243fbf28d5f38c10ebd77c8b986775975cc1d619efb27cdcd733fa1ca36cffe9c0a33cc9f02463c91a886601fd349efee85ef1462065ef9bd2c8f533220ad93138b8382d5938103ab25b2d9af8ae106e1211eb9b18793fba033900c809c02cd6d17e2f3e6fc84dae873411f8e87c3f0a8f1765b7825d185ce3730f299c3028d4a62da9ee95c2b870fb70c79370d485f9d5d9acb78926d20444033d960524d2776dc31988ec7c0dbf23b9905d" + +const ( + digits = iota + twain +) + +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 + // reasonably compressible. + digits: "testdata/e.txt.bz2", + // Twain is Project Gutenberg's edition of Mark Twain's classic English novel. + twain: "testdata/Mark.Twain-Tom.Sawyer.txt.bz2", +} + +func benchmarkDecode(b *testing.B, testfile int) { + compressed, err := ioutil.ReadFile(testfiles[testfile]) + if err != nil { + b.Fatal(err) + } + b.SetBytes(int64(len(compressed))) + for i := 0; i < b.N; i++ { + r := bytes.NewBuffer(compressed) + io.Copy(ioutil.Discard, NewReader(r)) + } +} + +func BenchmarkDecodeDigits(b *testing.B) { benchmarkDecode(b, digits) } +func BenchmarkDecodeTwain(b *testing.B) { benchmarkDecode(b, twain) } + +func TestBufferOverrun(t *testing.T) { + // Tests https://code.google.com/p/go/issues/detail?id=5747. + buffer := bytes.NewBuffer([]byte(bufferOverrunBase64)) + decoder := base64.NewDecoder(base64.StdEncoding, buffer) + decompressor := NewReader(decoder) + // This shouldn't panic. + ioutil.ReadAll(decompressor) +} + +var bufferOverrunBase64 string = ` +QlpoNTFBWSZTWTzyiGcACMP/////////////////////////////////3/7f3/// +////4N/fCZODak2Xo44GIHZgkGzDRbFAuwAAKoFV7T6AO6qwA6APb6s2rOoAkAAD +oACUoDtndh0iQAPkAAAAaPWihQoCgr5t97Obju21ChQB0NBm3RbA7apXrRoBooAA +AhA+IAHWl2Us3O7t9yieb3udvd76+4+fd33nd3HO1bVvfcGRne6+3vfPvfc++995 +w7k973eJhasLVec970tzDNXdX28LoPXZ3H3K9z0s5ufWAfes49d5594c3dUYtI+2 ++h1dvtpRa+uvrVEAG9bl893RVEN7cWvroSqWjPMGgAQi7Gq8TJSgKKdjKFBIB9Ae +LqWxleu715eXe7ml9e5098Z6G1vr7t1QZ6ot76YzPd3j7333t2ql2Chm7XrA9ICQ +VF77z3rVBWqkSXtlfb099hyezAr6USbGpICTSCFAaqHrKo+tUnm32rpE4Ue+t2mj +bKUeipEqwc93EdhhTwmQpOhhesC9iqDSPNTWYNSnUtBdm1nsA0nqqNd7OWwDXtFL +ONmmA6Ubke26I9UblvWIPR5VOWOnctai443URunnDy77uVC59OfRvezlDu33Z7Ly +3NNuuHW63088xu3t3NHZhkZbG7tXRlj00qOtbaXTJUUdspTbABR9R6EUwQAEAAAA +EMEwRpoAAAABMmhoAAjBNNAaCMhponpoGpgJpk9TEyp6niGKZkAaAEfqMQ09U80p ++pMGSCKngIAAAAgAAg0AAJhGgABGCEaaTyTKeNI1PE0wkj01GajMSNPSZGnqbU9T +anlPUNAHqGQ0DQAMg9TamgAAYRU/IAAICAmjQJgjQBMEwp5DTSaaYmhTeqfplPID +U1T9TynoU82pT1NPU/VP0j1NHqRpk9TTR7SnqaNNGmmQAaAD1Aeo0PSAAAAaaBiK +eBAQBGgIABGQA0AmBNNBoaAgaJmpglPEyYap6npiTT0agGjJjUaaDTQAAAAAAM1A +9QAaAAAADU8iEAQAEyAJk0NNNJgIZTJ5E00YSemiaZNGm1MpGNJ+lPU9qm9U2RDM +oY0EzJB6h6nqDID1NMBDDRpo1AGNAjCMmhkMgaYSJIgAAAQyAAEyBoATECCNhTT0 +U/IZAmCM1DSTxkzUE8p6NDaGiZGJqntTFHvUyU9qPQp7Kn5GgKNPU9QAGg9QAAA3 +wz0Pk/g/m/m9P9H4vxv2+dH3gCS8nhbbbbbYxtgNsBsG0m2MbG0NNtsbYNsaY0wb +bBibGmm22mxptNpsaGNDTY02JsG0MY0xg2MaYNNDbGwG0L5vsK/F9DO+EAA447Kq +p7Wdf6Y+5c20T7DfHyMXIzRKrZexw72uiQI+y55vOe52xpqbCLC2uR20JdER7Zvr +7ufuKb6zhiBxLuj0eA27v8RpMLucw9Ohwcizi2wrpt+yU1FdpM7ZYPcwS3XTef+A +Wzjxwhdrgw3aH1LeC1eZW900x8V9Nv4hTPXp4l067P/4ANVZFF/imOe/d5bdueam +/DFFokQWnFaU+ZqLBCM+d0PialJQWnLqRQZk/KhfbbYc2pCUTgffcSYbrCM1N+8l +HU6gSz+h2GJXs+tbrNviL83M97X0vcTn/F82P8wen8/3/h3sHY+sf9CSej9ThYTV +3lQ+FUHpfpGD4kv7dYMV995dpDX/y3xR8FoXx1bjUxBTNxuutwQ/h/Eedn9wpn6w +E3+ND8YhN1HSriIxRE/6uFyMv6/oC6Elarw3aHMMqHJkGiiz6tejmvnYLQa+Qm6G +deZ7jXTZV6NlpocgDnRdimS06bTYSkvPAL/xoWNLkX6N6VljU0dfKSBmm2uZE/xu +sutQ1EdP7GdjhglIq4xlOFUFEQpmX+xx7R8y6c0GSAaqusOjNZwxZRudOvmXm1tZ +T+YnbeB2ir9eiHNrtJNSLD/J/WDyuQpwBUtLKo0krccY/wIILP7f86teb9Z/9oyz +OX05qEWbObfhpRw+9+rCvp/35ML8KX3aHaI0n+tudbFRsV5FLW+Oa8ruLN4peyVL +DWjTHrXNthq/s7zAJYMeFJZkZt5mT9rfpH+5g3nc+piOSZ+J5nHtOnKI7Ff8Xl+j +0t76XTNucCHQ6whav1OHdF53TY5wuv5OzvrdnxoId8fTyUvERr0ERINu/8XxZZ5f +B5/kTZ8bBO0wv54Jp+ED/GQI8lZHzIQCP3vfQhwnCTj9TvITic7P4mYLDbH3fyzR +i+6EajCcpXLWSGf+ZXkOrWspDWDhXtEKas0v3UqWksqgY1rTj45krX4KihN+daXs +pZl5WPlta5p06CX6Xm2SfzqkMw12/3ix1bpnnZ+kFeBNX7A+E9zzG6OZaN78GOpl +9Ht/eZn9PqWdav852zr0zqkDK2H5IjdvNah+b1YVGdQGzwR4Nw+f13yEKnV+y66W +djfq7zWp7m5w+hzfv+Ly8O7oet5Vvd8/wQvO7qzOZ2vjf9X8Tj8PnMb/nc/nKqRR ++ml4UEhOOwfCeJEEI109CMYSh91iAJqPjMyH6KjrPD7W25llZVcREYNCTg6htbQt +M38wYoquCWP6tdKYlVIv14xTNUeUf4El/FunCf6csZkmv+9tfWx7t59wuKIa3saU +tZs9M+3HFOZtz3OLg/Unoaj9BYazYqA78xBU9tZzrtmF/rQL9CGJt90o/oYnSfcS +SL3haaw351LXWQ1XOsv1SmH3v6ymuxEpPPnEDmBELaTYsvvMIWJsmPZFFww++Kd7 +s/Jo0JFeUU7uNtI+gVosAIpVVuWfI/9tOIycz7I5Z7zjV+NR2OuZbYtW5F08KX4o +2k/xuJIchcNFPtxPfw9dkDgscRbMckyFMrzuZ3IvrcGzk0J6iI5ytrv37bGpAXMz +WK9mMMPebepNevmLjjo/QWoM968Sjv7ldlPS5AinHcXwsFv6dmmh8lJt7UOJWoKu +lMD1cB2ksIGpMdv8iuqR42Rn/kn+17BhhUZcwDBaUXVdX6bKW7fxlUYbq+mlqIcf +a9v8HF87M9ANbi9bq9onf9TD7nQ6Xf6vZci8TBPX+/GI0He6j31fTVQYW+NsQxvO +J8xrx+e58CCLQNjxeIyPt+F+qk/QMiXw+LyxGVkV/XcGQT9X03jSDP6beJ5QG1JW +9Q3qLv/YixWI7gPV9Mrhf2oRYTc/9KLFRhkE3SjKOTKuSSBKQ24fI+hEznamH71D +66Hwez8/0et7AtTv9zvamv2OD5He6fMV4k+ePl6+qPfO5CdHtK+eCDZL5+4f5yrl +gTcRFiq8fXbc5IaI5fbbc1KMM/2T0Mr7+Hwaco6FtXm0fmhCgTZRqY4pKiEIfmaz +QwHNOOCrtMJ2VwsyMumt7xsOolGnizRev6lILH43qPcczQM7Gc5zRin80YvFt1Qm +h/57Z0auR2h0fuX50MBO4XQ+26y5l6v4j902R66c0j3z2KHstKQ04J/h6LbuNQE4 +D6cu/lyfK69DxxX8wb8XaQkMUcJdo1LzqUGDAb3Kfn/A3P/JYc99MO9qv67+SxWb +wYTyqKdWTd+1KbR/Rcn0Io5zI/QquX7FA1bxfMytjQ/X+l0fh0Pf+Hx97meH4fQL +7/T8/sdTm9Tn8nELvedyhydLlPPTScINdXyLIq9wgIJr4fWPbp9ZhFh/56fdSgOG +HDXg+gkXsN2Rddr4HQ5P3u+RhLzmSjhzoqY5EsPC4QvRlX9JXjB84rPV5USR66qa +/kjw4156GJnzoXtydKJE53t6PHfZWO+3ujsfI6iAdshc7OFzGXiZB9PtItKodhYq +nABkTKdcpu4+TOpf9h5piX5slsaBjkeTnj/Ba02ilboQfcDVigxrYn/iTH5ySWUW +/lHtg78s5UZM8sErwhNe3N3w+6ZOMnU+5i86/xFNtqZfDdXTGy1H3PzGbdtZXYT+ +Ixx2vpwBYzbPVYHxKosM5rPiVmcTllI9nuoSfeh9ib4foFWauOpvdmhBDqpTpKTX +u8EO2l2Z195G2RIV7TlKSxGWjR5sl/nALu1uzBeLd9zpSujzMTd1uTX9Qk/Q1S+r +vaW6bm8qqPO4jb6Wx6XIkm321nrIF6Ae25d1+Dpv/P5G4NoLd2j6/EtENC3FeR5z +oo7bA+tI8yEQRhiF0z1FlJXLD5ZbhNNWQm/j/IbzRfh8JtOFZU7ruShLvHXysW9S +9V909tr9jn8/E/Hb5N/1NVNHnZu2HIUvJvHJiHd2ucmeI9PWUMnppmE65GQ5E9xV +ZRlGEH0X85EvmHyEupkMrCC0oMv9RCq+/H8gcfpe00Hs/S+regT5p58cyYomh93v +qvuw/A06BE/wzJESuYbN9pqYpoXqXFemW1NksHEJ2w+PYMJ27WJyD5FpaXB85VaW +qMOhDfO8E3QdH8ybyKt/UgI8/tDGpFbyOlaVdIv1FXJhoLp8soAA4Djg6/KZ066N +ZFYuS8WdjpSZGP4/Lw+1yaXlzNznc/k2uHe2uXP3uFuPcHx+Dm44utxldoO1uBPy ++jzOs14+MIgOjOHMVNqAbMd8fUedLlhJMCfMtm4uz01enLNKcMrtLlPIR37Yukh1 +YEMXYpm7eU4XU+j+Jj3pDyaXtXs+p1fWfTN/cy9/Oxs4umUXQ4uHh1kObtayDJ56 +/QMxiHobjHNKuKfMxsrYEwN+QVIyVjAwMDYuMjQ1AAA9IwJniiBLRkZDAAAXt0Ja +aDQxQVkmU1lZtwytAACLf///////////////////+//////v//////////bv78// +/+AXO133uwO2xB2UxIvbKXrCqCoURUBL2ytFI82AFdcOwMhVTHtk5rD3szEVNYD4 +aIQINCaMRoTaSn7SbSMJiYmEwieTEp+psqbMCp+VNPaFNpqbBNR7UmanlPUeKfqm +j1PU0/VPU08o9Q9EeKHlPJtKbYqeTCYhN6U9T1NH6mp+lPyoGNTI/Knkyg1MggAg +CaMEyQnqZoaaRtRtJpppppoDaTR6hpphGh6mmgHpMQBpkGTTEAAaAAAA00AZDag0 +ADIBkGgABqemiRNTI0k8aU0PRGRoAZlP0UAAAGgAAAyAADQaAAAaAAAAAAAAAAAA +AaAAAAM0kgRBJ5MlPFP1Gj0jTTTUaekxNAbUGjTQMgaZANNAAAAaAADTQAAAAAAA +ANAA0AAANADQ0QAAAAAAAAAaGgAAAAAAABoA0AAA0AAAAAAAAAAAAANAAAAAkSEI +aTRpomp5DUxNNDTJPTKaep6T09Kemmo2JG0aTQ9ENogaaGhkABo0NHqaBoDTI0DC +Gj0gNAMhoDQ9QMQNAGQAaDDwyMPIMlbG1vhRBTFo6JksSupgpAjPbY0ec02IGXjb +eS+FBsh01+O4ZOaD+srUZCFaT4DRjVDLx7uKIsFtESIDUg1ZkhyCSYov05C00MtR +BdNNa/AYPGOQZWcs+VegXOPrkushFbZ3mBoRD6WamClkpBaHZrUhUl02bIfRXX4w +b3/9cW9nHDVxh2qFBxqgRKfmq7/Jc/tdJk05nVrGbckGVy2PnIy30CDhpWmqrSot +K2bOnX0NbP1iy2cd0Na0ZmbRstm4MzMzbbMySTd35F7f+zPP8DC+NJLYcakkkkRd +NZlupJt3OMFoDAD2g+N3FAMCydhIpoRHRQAdFI5nNg4ugEXHCYxkMyGCwtaJmial +y0IMlpSYYM/weXNJAhFqS0GNmvaPEtYGjbvaucMdklOTmBX1vfVAkTYB1uXCSK64 +UNIixOqRKLuRCFtqIQtgwqaFrCkIYbbewErWABa+VGADWsJXJjfx5SJViLuwiGXq +Ru6vCuwmU5CJiJz3UiBpmLv0r2wskxUhY4tzPVGQ9RMXJl65eLSNwZVwaSyGZ9Cm +A3jztQUUpFeUryBTskW95iVwRMFrhBCwZBAFJBZvhMEMNoDJJlUoIhQkAkjbExp2 +YZio+ZYeAZUwmH1qUbdQixmxf0+61+aVgJ1hwxsO1yG3hFx4pfjc09ITVht0pG8u +FtVFhPa1KE0gTRUSVXywkITucqk0Waz5Fs6qJpVHYdNrbYRFxnFsQGY1qmsTLjK6 +4QX5Rddo6krM/Bx9CqIAKq4CzVQYHrmIAd2EBhYmwVYwLvhzKIUrc2EirnGIvyuD +O4YZDSwsVTA0BpVvUOjDErkCraBoSutcKwUSSLGhVvNYHLz3klgZD++wWsa/swLw +gvNDY2De+sncOv8X2lq4HD95ZdwPuTIMXCwSbg4RrIqv+L0y6F17pqDecyQYPEj3 +iN/0BBeWZlJAyBMi5U3Q1zAlsK8IlDhaXGmvZrgISq5CfNjmUgxDeMggOKqxu4sI +OrilS49Lkl1J3u3GjXTuH+rX+4ccyFAQnizCpPClcY77F59j63S6fr5vr+y99tuO +7Ox7Wg/ljwhdyaK4xMmXczeJbx7x07htJNtC4xcQfAtvzeznLrN6MN/ILIBOI65I +qIA2D5fHHj1XN4aN6TvOjWDaSbSWqxCSCvXUpzkNJAkWXAuTwF8k5uSJvQj/rVo0 +hAhEMEIYkCRGx9AX+byIuXWlLMbbVeliHNUL5AQYmNwLFu4SkmGD+UWtBMyVHQOQ +ss0ggoVKSKOBUgnVS6ljt7WE1qXqJJ4QA1pEwYNLEaguEE1LtPNoVr5WzjbSbWPk +V9OW3y9IneUDLoIV5pAkEFTEFGFVjeTFxtpzBBfGgycBxVCdz8eESBIzsamRchAa +TQunQH8DHnpfod9QuAuRvc7JBlKUCYmCjMvynLcxIFohxCaYrDvGw4QbXZB7oWQ7 +hpoGlz23ayDfB8NrRRzdilsEQyQniu9ASLQg7RrGZnoTr1ai12IbCEUCGdFq03P5 +nBnRFAGmisQGcyykV9gKtcVMWLhCuVmXg86dndn7slUpRNSSEAU20oaWIm1maFTu +E0DT4gTbg0nuhjtz3kNOz+i7sBm0bkXjxQWuLqlZEmp60ZTyRZJDUqKSEKg6hqcy +ERxdU22CSNOO10RYUUiDVpKhPNdKTOIE1thp02sBNoNTFSht8WJtaBQ09qN3jd5r +dOLX4IA5fevRyCCzDgRXfV4wzik4KROjmxmTMglBySlIMEzcXehnDXCRiZSlvwA2 +0YsIOROcm4UrIRFxJHctJH7OdN5u1aHVHb5UaLHpv48NgmFRE56KTSoaWunqm2st +S0mrAdOiqcR12PWVbdVRJKcQ0DQuhwlAPcRtpxN3D4kbXJjToSYJIFw406G2CSaK +jQMIJPZGlQmgyFhoCSzeGS1VSq5SKKQQxs5RqKUcVUNY57YUETb4mXzV84SPngKi +nsce0mXByZq5BKUA9puHZWLNwQIYuDaJUNgG+E01E3pDYVNLKYQ0hsVesgV5gZY0 +htDsRdGtm0+iGnkN6+Ea9YJtUZNAkx2GgSoix12nTW0avTUfxR3oYcpvZ7IdtABE +UhBcjG4qZtDZsS1JQHys243vhLaDTSvvTeBiJA2tmokqECTBcSOCAGkAxMKlVAva +4IsLRaBBqhxDbcGtgdw03mFcLUaFuhtKuuEIEkUleJQwby/zwu9uvvZK4xTV+ECM +a8lmzxKmqkBggYK1+xPdbmJclm6tSZhE/OSJtCEjs+unJIQkT9hCWgBJqGMS07Eh +AJNmBiuVEVdTyjkIJkavuZmx2sJF13htgEZUCC23lZFOE6gWbM9WyYNJTM8yCQrb +0Sx3OQvBML5cRATAQkSQkAJOAhoxpQkNi4ZiEVDbdtJAME0RXNDXGHA3M3Q0mm1o +IEwbWpaM1DQCSMbGRCAu3iRIQiT6RlBpT1n3tfwvUXz3gIVlx3mEximY/kZW1kNG +sgEJIrBisaEoGYPJ+1CQUYFBw+eGEHJQBpNHjErXUJY2iWHQ30hXwFBuMSxQ2lB5 +bg+/LX3euG6HsHUB1lFvBvaiaBrITVwkCTa1d0s9CHZCiDZjbWReKyrpPE2oSa7o +LPrR4BJvys9ttjUpzETSSMxh8vsr9dXTwKBtK+1xCTGDQmNIaE29HmHdS5GSxpya +MismcAUSEgSxHBrKtgsZzduG7vHZn16l3kFkVITtENIzS2JsiBwFTDlhgexsjBHv +5HXOYxHBzoSDCcPZ0ctvkY9aS5XpoQuFYkGJgCsqjJZeUMNUEpDSbKcnUc1PifIA +CbR2UoXawBlspkEBr9HBfvUi/MUakZVOf1WKYrqSaIXce62JOyhJLq3qJBloTA0F +VbILEtM+heFmNRCFt70GJrExVJri0ArYbCRbADSGDBpBXxxb/6fo+s3C7uaL7RjM +LV2IQBNrAJrKFeJwTsPnxbAsemirUx2lk1kaxschzdK4TQNJN5wQnolIFg401OZ4 +2na11LnT3lR+1k1TMJhiAjXMk0F1ooHnYlt9LKfJ3ZIOmeY+2l9bUQHWFNGyEyfj +EAcu3kpGLq0Ez7XOS+EpAASRQTAYMATfVQibHLTT30zG732+pNe9za1JNt8sNJYn +RjWuJ6jL5ILV0rcd9vT7X9fObvcXitpvJ2XBJE+PhX2HaTkyWeF9pwnlQNrTe9hV +tzhA+ihZrDrHNmLcQjZbnv/IMubqq8egxY80t5n6vZ6U5TR6U9uZJvai1xtqAyCR +NWkW52m00rDTEuO6BA4q2RHDWwbETF55rRsWLIgNW9qJCyMHPbTM/dMBmWMQSMxz +4M2pRzt47SICxA327UqSCEERqMFybmYi3nUxePtLgHYplqRiw4ynMbXd/kiQ0LE0 +PKJSSCXA42ymziCpAxNWflzpzQdJZusahRFr6t6m+4p273/Taj7k+hZyNgBAgXAY +8F7pTts6orLb8IA6o4TOwkwQYmKvKu9VwMrE7+GUhVIAgY9a8DyQMiDBkEAwh7S1 +KgCBfao8DK1CwSS8Z3WjL5MEgt93z2koUQCD/YxMBppiCMp7SDVSmkkIHptfGpeh +t+M13Ccv1tavIASFiaQl6rBz3K4N3DSGwNkCibrvEAC0fQirOWnc4NVbcLKpFG1l +NQXF/eqdT79wq1Mvlap3QSCLhcD2D3fCkKVWid4aSjtp9FOX1Uaf7P9eT93zd9Sv +mj2yNLRUGzyI/0oONNSzmmkvJ5Cq2X2CdldIWMGZO57RJ8oyATAWTQmRmNkfh0Sx +uuR/J9oUsomVy1AEntc0dlPivkqBkBqrxU3j5PnWkaI3ZRGc0gg9spCQEISh4xEU +pMhVrnmDQLfLP8Ouqpx917MAw7hkjQk6BJFTAbXDsz3LSHIxo/gB8qrA1vbvdZZh +LtR0frJdfdppX8nAQX/TAxOQ8+H6yw8a9i7/zJEfSYIhop59N/fhcWW2F14cj2Xc +fyHaZ04lTO4uPnly91jwuFPaREuZVp8AxImIhlkxkAN61tWdWG7tEbaCgszh6VIz +ThFnHo2Vi8SQXPrXCN7J9Tc9ZYiAYqoThV/u6SYsea5aZL8deOvKBQCgZZuIxX1z +4EnfcqG176vY4VqMBIC4pMJz0WcHJYqN+j7BiwGoMBwExrIdTB7q4XIFLotcIpS0 +1MqyVsesvoQq7WObmGQXdMliMirSLcDuSx8Qy+4pIBgGDIyMp1qbonnGdcHYvU8S +O0A8s/iua5oFdNZTWvbVI4FUH9sKcLiB3/fIAF+sB4n8q6L+UCfmbPcAo/crQ6b3 +HqhDBMY9J0q/jdz9GNYZ/1fbXdkUqAQKFePhtzJDRBZba27+LPQNMCcrHMq06F1T +4QmLmkHt7LxB2pAczUO+T2O9bHEw/HWw+dYf2MoRDUw= +` diff --git a/libgo/go/compress/bzip2/huffman.go b/libgo/go/compress/bzip2/huffman.go index f755019..8f6b0c9 100644 --- a/libgo/go/compress/bzip2/huffman.go +++ b/libgo/go/compress/bzip2/huffman.go @@ -33,14 +33,17 @@ const invalidNodeValue = 0xffff // Decode reads bits from the given bitReader and navigates the tree until a // symbol is found. -func (t huffmanTree) Decode(br *bitReader) (v uint16) { +func (t *huffmanTree) Decode(br *bitReader) (v uint16) { nodeIndex := uint16(0) // node 0 is the root of the tree. for { node := &t.nodes[nodeIndex] - bit := br.ReadBit() + bit, ok := br.TryReadBit() + if !ok && br.ReadBit() { + bit = 1 + } // bzip2 encodes left as a true bit. - if bit { + if bit != 0 { // left if node.left == invalidNodeValue { return node.leftValue diff --git a/libgo/go/compress/bzip2/move_to_front.go b/libgo/go/compress/bzip2/move_to_front.go index 0ed19de..b7e75a7 100644 --- a/libgo/go/compress/bzip2/move_to_front.go +++ b/libgo/go/compress/bzip2/move_to_front.go @@ -15,10 +15,11 @@ type moveToFrontDecoder struct { // Rather than actually keep the list in memory, the symbols are stored // as a circular, double linked list with the symbol indexed by head // at the front of the list. - symbols []byte - next []uint8 - prev []uint8 + symbols [256]byte + next [256]uint8 + prev [256]uint8 head uint8 + len int } // newMTFDecoder creates a move-to-front decoder with an explicit initial list @@ -28,12 +29,9 @@ func newMTFDecoder(symbols []byte) *moveToFrontDecoder { panic("too many symbols") } - m := &moveToFrontDecoder{ - symbols: symbols, - next: make([]uint8, len(symbols)), - prev: make([]uint8, len(symbols)), - } - + m := new(moveToFrontDecoder) + copy(m.symbols[:], symbols) + m.len = len(symbols) m.threadLinkedList() return m } @@ -45,34 +43,29 @@ func newMTFDecoderWithRange(n int) *moveToFrontDecoder { panic("newMTFDecoderWithRange: cannot have > 256 symbols") } - m := &moveToFrontDecoder{ - symbols: make([]uint8, n), - next: make([]uint8, n), - prev: make([]uint8, n), - } - + m := new(moveToFrontDecoder) for i := 0; i < n; i++ { - m.symbols[i] = byte(i) + m.symbols[byte(i)] = byte(i) } - + m.len = n m.threadLinkedList() return m } // threadLinkedList creates the initial linked-list pointers. func (m *moveToFrontDecoder) threadLinkedList() { - if len(m.symbols) == 0 { + if m.len == 0 { return } - m.prev[0] = uint8(len(m.symbols) - 1) + m.prev[0] = uint8(m.len - 1) - for i := 0; i < len(m.symbols)-1; i++ { + for i := byte(0); int(i) < m.len-1; i++ { m.next[i] = uint8(i + 1) m.prev[i+1] = uint8(i) } - m.next[len(m.symbols)-1] = 0 + m.next[m.len-1] = 0 } func (m *moveToFrontDecoder) Decode(n int) (b byte) { diff --git a/libgo/go/compress/flate/copy.go b/libgo/go/compress/flate/copy.go index 06e5d2e..a3200a8 100644 --- a/libgo/go/compress/flate/copy.go +++ b/libgo/go/compress/flate/copy.go @@ -6,12 +6,27 @@ package flate // forwardCopy is like the built-in copy function except that it always goes // forward from the start, even if the dst and src overlap. -func forwardCopy(dst, src []byte) int { - if len(src) > len(dst) { - src = src[:len(dst)] +// It is equivalent to: +// for i := 0; i < n; i++ { +// mem[dst+i] = mem[src+i] +// } +func forwardCopy(mem []byte, dst, src, n int) { + if dst <= src { + copy(mem[dst:dst+n], mem[src:src+n]) + return } - for i, x := range src { - dst[i] = x + for { + if dst >= src+n { + copy(mem[dst:dst+n], mem[src:src+n]) + return + } + // There is some forward overlap. The destination + // will be filled with a repeated pattern of mem[src:src+k]. + // We copy one instance of the pattern here, then repeat. + // Each time around this loop k will double. + k := dst - src + copy(mem[dst:dst+k], mem[src:src+k]) + n -= k + dst += k } - return len(src) } diff --git a/libgo/go/compress/flate/copy_test.go b/libgo/go/compress/flate/copy_test.go index a9281d4..2011b15 100644 --- a/libgo/go/compress/flate/copy_test.go +++ b/libgo/go/compress/flate/copy_test.go @@ -30,10 +30,12 @@ func TestForwardCopy(t *testing.T) { } for _, tc := range testCases { b := []byte("0123456789") - dst := b[tc.dst0:tc.dst1] - src := b[tc.src0:tc.src1] - n := forwardCopy(dst, src) - got := string(dst[:n]) + n := tc.dst1 - tc.dst0 + if tc.src1-tc.src0 < n { + n = tc.src1 - tc.src0 + } + forwardCopy(b, tc.dst0, tc.src0, n) + got := string(b[tc.dst0 : tc.dst0+n]) if got != tc.want { t.Errorf("dst=b[%d:%d], src=b[%d:%d]: got %q, want %q", tc.dst0, tc.dst1, tc.src0, tc.src1, got, tc.want) diff --git a/libgo/go/compress/flate/deflate.go b/libgo/go/compress/flate/deflate.go index d357fe3..8c79df0 100644 --- a/libgo/go/compress/flate/deflate.go +++ b/libgo/go/compress/flate/deflate.go @@ -416,6 +416,50 @@ func (d *compressor) init(w io.Writer, level int) (err error) { return nil } +var zeroes [32]int +var bzeroes [256]byte + +func (d *compressor) reset(w io.Writer) { + d.w.reset(w) + d.sync = false + d.err = nil + switch d.compressionLevel.chain { + case 0: + // level was NoCompression. + for i := range d.window { + d.window[i] = 0 + } + d.windowEnd = 0 + default: + d.chainHead = -1 + for s := d.hashHead; len(s) > 0; { + n := copy(s, zeroes[:]) + s = s[n:] + } + for s := d.hashPrev; len(s) > 0; s = s[len(zeroes):] { + copy(s, zeroes[:]) + } + d.hashOffset = 1 + + d.index, d.windowEnd = 0, 0 + for s := d.window; len(s) > 0; { + n := copy(s, bzeroes[:]) + s = s[n:] + } + d.blockStart, d.byteAvailable = 0, false + + d.tokens = d.tokens[:maxFlateBlockTokens+1] + for i := 0; i <= maxFlateBlockTokens; i++ { + d.tokens[i] = 0 + } + d.tokens = d.tokens[:0] + d.length = minMatchLength - 1 + d.offset = 0 + d.hash = 0 + d.maxInsertIndex = 0 + } +} + func (d *compressor) close() error { d.sync = true d.step(d) @@ -439,7 +483,6 @@ func (d *compressor) close() error { // If level is in the range [-1, 9] then the error returned will be nil. // Otherwise the error returned will be non-nil. func NewWriter(w io.Writer, level int) (*Writer, error) { - const logWindowSize = logMaxOffsetSize var dw Writer if err := dw.d.init(w, level); err != nil { return nil, err @@ -462,6 +505,7 @@ func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) { zw.Write(dict) zw.Flush() dw.enabled = true + zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method. return zw, err } @@ -480,7 +524,8 @@ func (w *dictWriter) Write(b []byte) (n int, err error) { // A Writer takes data written to it and writes the compressed // form of that data to an underlying writer (see NewWriter). type Writer struct { - d compressor + d compressor + dict []byte } // Write writes data to w, which will eventually write the @@ -506,3 +551,21 @@ func (w *Writer) Flush() error { func (w *Writer) Close() error { return w.d.close() } + +// Reset discards the writer's state and makes it equivalent to +// the result of NewWriter or NewWriterDict called with dst +// and w's level and dictionary. +func (w *Writer) Reset(dst io.Writer) { + if dw, ok := w.d.w.w.(*dictWriter); ok { + // w was created with NewWriterDict + dw.w = dst + w.d.reset(dw) + dw.enabled = false + w.Write(w.dict) + w.Flush() + dw.enabled = true + } else { + // w was created with NewWriter + w.d.reset(dst) + } +} diff --git a/libgo/go/compress/flate/deflate_test.go b/libgo/go/compress/flate/deflate_test.go index 8c4a6d6..730234c 100644 --- a/libgo/go/compress/flate/deflate_test.go +++ b/libgo/go/compress/flate/deflate_test.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/ioutil" + "reflect" "sync" "testing" ) @@ -424,3 +425,66 @@ func TestRegression2508(t *testing.T) { } w.Close() } + +func TestWriterReset(t *testing.T) { + for level := 0; level <= 9; level++ { + if testing.Short() && level > 1 { + break + } + w, err := NewWriter(ioutil.Discard, level) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } + buf := []byte("hello world") + for i := 0; i < 1024; i++ { + w.Write(buf) + } + w.Reset(ioutil.Discard) + + wref, err := NewWriter(ioutil.Discard, level) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } + + // DeepEqual doesn't compare functions. + w.d.fill, wref.d.fill = nil, nil + w.d.step, wref.d.step = nil, nil + if !reflect.DeepEqual(w, wref) { + t.Errorf("level %d Writer not reset after Reset", level) + } + } + testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, NoCompression) }) + testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, DefaultCompression) }) + testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriter(w, BestCompression) }) + dict := []byte("we are the world") + testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, NoCompression, dict) }) + testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, DefaultCompression, dict) }) + testResetOutput(t, func(w io.Writer) (*Writer, error) { return NewWriterDict(w, BestCompression, dict) }) +} + +func testResetOutput(t *testing.T, newWriter func(w io.Writer) (*Writer, error)) { + buf := new(bytes.Buffer) + w, err := newWriter(buf) + if err != nil { + t.Fatalf("NewWriter: %v", err) + } + b := []byte("hello world") + for i := 0; i < 1024; i++ { + w.Write(b) + } + w.Close() + out1 := buf.String() + + buf2 := new(bytes.Buffer) + w.Reset(buf2) + for i := 0; i < 1024; i++ { + w.Write(b) + } + w.Close() + out2 := buf2.String() + + if out1 != out2 { + t.Errorf("got %q, expected %q", out2, out1) + } + t.Logf("got %d bytes", len(out1)) +} diff --git a/libgo/go/compress/flate/flate_test.go b/libgo/go/compress/flate/flate_test.go index aba820a..57fea5a 100644 --- a/libgo/go/compress/flate/flate_test.go +++ b/libgo/go/compress/flate/flate_test.go @@ -24,3 +24,39 @@ func TestUncompressedSource(t *testing.T) { t.Errorf("output[0] = %x, want 0x11", output[0]) } } + +// The following test should not panic. +func TestIssue5915(t *testing.T) { + bits := []int{4, 0, 0, 6, 4, 3, 2, 3, 3, 4, 4, 5, 0, 0, 0, 0, 5, 5, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 6, 0, 11, 0, 8, 0, 6, 6, 10, 8} + h := new(huffmanDecoder) + ok := h.init(bits) + if ok == true { + t.Fatalf("Given sequence of bits is bad, and should not succeed.") + } +} + +// The following test should not panic. +func TestIssue5962(t *testing.T) { + bits := []int{4, 0, 0, 6, 4, 3, 2, 3, 3, 4, 4, 5, 0, 0, 0, 0, + 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11} + h := new(huffmanDecoder) + ok := h.init(bits) + if ok == true { + t.Fatalf("Given sequence of bits is bad, and should not succeed.") + } +} + +// The following test should not panic. +func TestIssue6255(t *testing.T) { + bits1 := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11} + bits2 := []int{11, 13} + h := new(huffmanDecoder) + if !h.init(bits1) { + t.Fatalf("Given sequence of bits is good and should succeed.") + } + if h.init(bits2) { + t.Fatalf("Given sequence of bits is bad and should not succeed.") + } +} diff --git a/libgo/go/compress/flate/huffman_bit_writer.go b/libgo/go/compress/flate/huffman_bit_writer.go index 25e1da3..b182a71 100644 --- a/libgo/go/compress/flate/huffman_bit_writer.go +++ b/libgo/go/compress/flate/huffman_bit_writer.go @@ -97,6 +97,31 @@ func newHuffmanBitWriter(w io.Writer) *huffmanBitWriter { } } +func (w *huffmanBitWriter) reset(writer io.Writer) { + w.w = writer + w.bits, w.nbits, w.nbytes, w.err = 0, 0, 0, nil + w.bytes = [64]byte{} + for i := range w.codegen { + w.codegen[i] = 0 + } + for _, s := range [...][]int32{w.literalFreq, w.offsetFreq, w.codegenFreq} { + for i := range s { + s[i] = 0 + } + } + for _, enc := range [...]*huffmanEncoder{ + w.literalEncoding, + w.offsetEncoding, + w.codegenEncoding} { + for i := range enc.code { + enc.code[i] = 0 + } + for i := range enc.codeBits { + enc.codeBits[i] = 0 + } + } +} + func (w *huffmanBitWriter) flushBits() { if w.err != nil { w.nbits = 0 diff --git a/libgo/go/compress/flate/huffman_code.go b/libgo/go/compress/flate/huffman_code.go index 009cce6..3b9fce4 100644 --- a/libgo/go/compress/flate/huffman_code.go +++ b/libgo/go/compress/flate/huffman_code.go @@ -19,23 +19,13 @@ type literalNode struct { freq int32 } -type chain struct { - // The sum of the leaves in this tree - freq int32 - - // The number of literals to the left of this item at this level - leafCount int32 - - // The right child of this chain in the previous level. - up *chain -} - +// A levelInfo describes the state of the constructed tree for a given depth. type levelInfo struct { // Our level. for better printing level int32 - // The most recent chain generated for this level - lastChain *chain + // The frequency of the last node at this level + lastFreq int32 // The frequency of the next character to add to this level nextCharFreq int32 @@ -47,12 +37,6 @@ type levelInfo struct { // The number of chains remaining to generate for this level before moving // up to the next level needed int32 - - // The levelInfo for level+1 - up *levelInfo - - // The levelInfo for level-1 - down *levelInfo } func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxInt32} } @@ -121,6 +105,8 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 { return total } +const maxBitsLimit = 16 + // Return the number of literals assigned to each bit size in the Huffman encoding // // This method is only called when list.length >= 3 @@ -131,9 +117,13 @@ func (h *huffmanEncoder) bitLength(freq []int32) int64 { // frequency, and has as its last element a special element with frequency // MaxInt32 // maxBits The maximum number of bits that should be used to encode any literal. +// Must be less than 16. // return An integer array in which array[i] indicates the number of literals // that should be encoded in i bits. func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { + if maxBits >= maxBitsLimit { + panic("flate: maxBits too large") + } n := int32(len(list)) list = list[0 : n+1] list[n] = maxNode() @@ -148,53 +138,61 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { // A bogus "Level 0" whose sole purpose is so that // level1.prev.needed==0. This makes level1.nextPairFreq // be a legitimate value that never gets chosen. - top := &levelInfo{needed: 0} - chain2 := &chain{list[1].freq, 2, new(chain)} + var levels [maxBitsLimit]levelInfo + // leafCounts[i] counts the number of literals at the left + // of ancestors of the rightmost node at level i. + // leafCounts[i][j] is the number of literals at the left + // of the level j ancestor. + var leafCounts [maxBitsLimit][maxBitsLimit]int32 + for level := int32(1); level <= maxBits; level++ { // For every level, the first two items are the first two characters. // We initialize the levels as if we had already figured this out. - top = &levelInfo{ + levels[level] = levelInfo{ level: level, - lastChain: chain2, + lastFreq: list[1].freq, nextCharFreq: list[2].freq, nextPairFreq: list[0].freq + list[1].freq, - down: top, } - top.down.up = top + leafCounts[level][level] = 2 if level == 1 { - top.nextPairFreq = math.MaxInt32 + levels[level].nextPairFreq = math.MaxInt32 } } // We need a total of 2*n - 2 items at top level and have already generated 2. - top.needed = 2*n - 4 + levels[maxBits].needed = 2*n - 4 - l := top + level := maxBits for { + l := &levels[level] if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 { // We've run out of both leafs and pairs. // End all calculations for this level. - // To m sure we never come back to this level or any lower level, + // To make sure we never come back to this level or any lower level, // set nextPairFreq impossibly large. - l.lastChain = nil l.needed = 0 - l = l.up - l.nextPairFreq = math.MaxInt32 + levels[level+1].nextPairFreq = math.MaxInt32 + level++ continue } - prevFreq := l.lastChain.freq + prevFreq := l.lastFreq if l.nextCharFreq < l.nextPairFreq { // The next item on this row is a leaf node. - n := l.lastChain.leafCount + 1 - l.lastChain = &chain{l.nextCharFreq, n, l.lastChain.up} + n := leafCounts[level][level] + 1 + l.lastFreq = l.nextCharFreq + // Lower leafCounts are the same of the previous node. + leafCounts[level][level] = n l.nextCharFreq = list[n].freq } else { // The next item on this row is a pair from the previous row. // nextPairFreq isn't valid until we generate two // more values in the level below - l.lastChain = &chain{l.nextPairFreq, l.lastChain.leafCount, l.down.lastChain} - l.down.needed = 2 + l.lastFreq = l.nextPairFreq + // Take leaf counts from the lower level, except counts[level] remains the same. + copy(leafCounts[level][:level], leafCounts[level-1][:level]) + levels[l.level-1].needed = 2 } if l.needed--; l.needed == 0 { @@ -202,33 +200,33 @@ func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 { // Continue calculating one level up. Fill in nextPairFreq // of that level with the sum of the two nodes we've just calculated on // this level. - up := l.up - if up == nil { + if l.level == maxBits { // All done! break } - up.nextPairFreq = prevFreq + l.lastChain.freq - l = up + levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq + level++ } else { // If we stole from below, move down temporarily to replenish it. - for l.down.needed > 0 { - l = l.down + for levels[level-1].needed > 0 { + level-- } } } // Somethings is wrong if at the end, the top level is null or hasn't used // all of the leaves. - if top.lastChain.leafCount != n { - panic("top.lastChain.leafCount != n") + if leafCounts[maxBits][maxBits] != n { + panic("leafCounts[maxBits][maxBits] != n") } bitCount := make([]int32, maxBits+1) bits := 1 - for chain := top.lastChain; chain.up != nil; chain = chain.up { + counts := &leafCounts[maxBits] + for level := maxBits; level > 0; level-- { // chain.leafCount gives the number of literals requiring at least "bits" // bits to encode. - bitCount[bits] = chain.leafCount - chain.up.leafCount + bitCount[bits] = counts[level] - counts[level-1] bits++ } return bitCount diff --git a/libgo/go/compress/flate/inflate.go b/libgo/go/compress/flate/inflate.go index beca34b..3eb3b2b 100644 --- a/libgo/go/compress/flate/inflate.go +++ b/libgo/go/compress/flate/inflate.go @@ -91,6 +91,10 @@ type huffmanDecoder struct { // Initialize Huffman decoding tables from array of code lengths. func (h *huffmanDecoder) init(bits []int) bool { + if h.min != 0 { + *h = huffmanDecoder{} + } + // Count number of codes of each length, // compute min and max length. var count [maxCodeLen]int @@ -125,6 +129,9 @@ func (h *huffmanDecoder) init(bits []int) bool { if i == huffmanChunkBits+1 { // create link tables link := code >> 1 + if huffmanNumChunks < link { + return false + } h.links = make([][]uint32, huffmanNumChunks-link) for j := uint(link); j < huffmanNumChunks; j++ { reverse := int(reverseByte[j>>8]) | int(reverseByte[j&0xff])<<8 @@ -154,7 +161,11 @@ func (h *huffmanDecoder) init(bits []int) bool { h.chunks[off] = chunk } } else { - linktab := h.links[h.chunks[reverse&(huffmanNumChunks-1)]>>huffmanValueShift] + value := h.chunks[reverse&(huffmanNumChunks-1)] >> huffmanValueShift + if value >= uint32(len(h.links)) { + return false + } + linktab := h.links[value] reverse >>= huffmanChunkBits for off := reverse; off < numLinks; off += 1 << uint(n-huffmanChunkBits) { linktab[off] = chunk @@ -511,7 +522,7 @@ func (f *decompressor) copyHist() bool { if x := len(f.hist) - p; n > x { n = x } - forwardCopy(f.hist[f.hp:f.hp+n], f.hist[p:p+n]) + forwardCopy(f.hist[:], f.hp, p, n) p += n f.hp += n f.copyLen -= n @@ -633,6 +644,10 @@ func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) { if n > huffmanChunkBits { chunk = h.links[chunk>>huffmanValueShift][(f.b>>huffmanChunkBits)&h.linkMask] n = uint(chunk & huffmanCountMask) + if n == 0 { + f.err = CorruptInputError(f.roffset) + return 0, f.err + } } if n <= f.nb { f.b >>= n diff --git a/libgo/go/compress/flate/reader_test.go b/libgo/go/compress/flate/reader_test.go index 54ed788..2a8ebbc 100644 --- a/libgo/go/compress/flate/reader_test.go +++ b/libgo/go/compress/flate/reader_test.go @@ -37,6 +37,7 @@ var testfiles = []string{ } func benchmarkDecode(b *testing.B, testfile, level, n int) { + b.ReportAllocs() b.StopTimer() b.SetBytes(int64(n)) buf0, err := ioutil.ReadFile(testfiles[testfile]) @@ -55,7 +56,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) { if len(buf0) > n-i { buf0 = buf0[:n-i] } - io.Copy(w, bytes.NewBuffer(buf0)) + io.Copy(w, bytes.NewReader(buf0)) } w.Close() buf1 := compressed.Bytes() @@ -63,7 +64,7 @@ func benchmarkDecode(b *testing.B, testfile, level, n int) { runtime.GC() b.StartTimer() for i := 0; i < b.N; i++ { - io.Copy(ioutil.Discard, NewReader(bytes.NewBuffer(buf1))) + io.Copy(ioutil.Discard, NewReader(bytes.NewReader(buf1))) } } diff --git a/libgo/go/compress/gzip/gunzip_test.go b/libgo/go/compress/gzip/gunzip_test.go index a133358..572fb58 100644 --- a/libgo/go/compress/gzip/gunzip_test.go +++ b/libgo/go/compress/gzip/gunzip_test.go @@ -7,7 +7,10 @@ package gzip import ( "bytes" "io" + "io/ioutil" + "os" "testing" + "time" ) type gunzipTest struct { @@ -302,3 +305,31 @@ func TestDecompressor(t *testing.T) { } } } + +func TestIssue6550(t *testing.T) { + f, err := os.Open("testdata/issue6550.gz") + if err != nil { + t.Fatal(err) + } + gzip, err := NewReader(f) + if err != nil { + t.Fatalf("NewReader(testdata/issue6550.gz): %v", err) + } + defer gzip.Close() + done := make(chan bool, 1) + go func() { + _, err := io.Copy(ioutil.Discard, gzip) + if err == nil { + t.Errorf("Copy succeeded") + } else { + t.Logf("Copy failed (correctly): %v", err) + } + done <- true + }() + select { + case <-time.After(1 * time.Second): + t.Errorf("Copy hung") + case <-done: + // ok + } +} diff --git a/libgo/go/compress/gzip/gzip.go b/libgo/go/compress/gzip/gzip.go index 45558b7..fe32d68 100644 --- a/libgo/go/compress/gzip/gzip.go +++ b/libgo/go/compress/gzip/gzip.go @@ -26,14 +26,15 @@ const ( // to its wrapped io.Writer. type Writer struct { Header - w io.Writer - level int - compressor *flate.Writer - digest hash.Hash32 - size uint32 - closed bool - buf [10]byte - err error + w io.Writer + level int + wroteHeader bool + compressor *flate.Writer + digest hash.Hash32 + size uint32 + closed bool + buf [10]byte + err error } // NewWriter creates a new Writer that satisfies writes by compressing data @@ -62,14 +63,39 @@ func NewWriterLevel(w io.Writer, level int) (*Writer, error) { if level < DefaultCompression || level > BestCompression { return nil, fmt.Errorf("gzip: invalid compression level: %d", level) } - return &Writer{ + z := new(Writer) + z.init(w, level) + return z, nil +} + +func (z *Writer) init(w io.Writer, level int) { + digest := z.digest + if digest != nil { + digest.Reset() + } else { + digest = crc32.NewIEEE() + } + compressor := z.compressor + if compressor != nil { + compressor.Reset(w) + } + *z = Writer{ Header: Header{ OS: 255, // unknown }, - w: w, - level: level, - digest: crc32.NewIEEE(), - }, nil + w: w, + level: level, + digest: digest, + compressor: compressor, + } +} + +// Reset discards the Writer z's state and makes it equivalent to the +// result of its original state from NewWriter or NewWriterLevel, but +// writing to w instead. This permits reusing a Writer rather than +// allocating a new one. +func (z *Writer) Reset(w io.Writer) { + z.init(w, z.level) } // GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950). @@ -138,7 +164,8 @@ func (z *Writer) Write(p []byte) (int, error) { } var n int // Write the GZIP header lazily. - if z.compressor == nil { + if !z.wroteHeader { + z.wroteHeader = true z.buf[0] = gzipID1 z.buf[1] = gzipID2 z.buf[2] = gzipDeflate @@ -183,7 +210,9 @@ func (z *Writer) Write(p []byte) (int, error) { return n, z.err } } - z.compressor, _ = flate.NewWriter(z.w, z.level) + if z.compressor == nil { + z.compressor, _ = flate.NewWriter(z.w, z.level) + } } z.size += uint32(len(p)) z.digest.Write(p) @@ -206,8 +235,11 @@ func (z *Writer) Flush() error { if z.closed { return nil } - if z.compressor == nil { + if !z.wroteHeader { z.Write(nil) + if z.err != nil { + return z.err + } } z.err = z.compressor.Flush() return z.err @@ -222,7 +254,7 @@ func (z *Writer) Close() error { return nil } z.closed = true - if z.compressor == nil { + if !z.wroteHeader { z.Write(nil) if z.err != nil { return z.err diff --git a/libgo/go/compress/gzip/gzip_test.go b/libgo/go/compress/gzip/gzip_test.go index 4d1af94..119be2e 100644 --- a/libgo/go/compress/gzip/gzip_test.go +++ b/libgo/go/compress/gzip/gzip_test.go @@ -197,3 +197,35 @@ func TestWriterFlush(t *testing.T) { t.Fatal("Flush didn't flush any data") } } + +// Multiple gzip files concatenated form a valid gzip file. +func TestConcat(t *testing.T) { + var buf bytes.Buffer + w := NewWriter(&buf) + w.Write([]byte("hello ")) + w.Close() + w = NewWriter(&buf) + w.Write([]byte("world\n")) + w.Close() + + r, err := NewReader(&buf) + data, err := ioutil.ReadAll(r) + if string(data) != "hello world\n" || err != nil { + t.Fatalf("ReadAll = %q, %v, want %q, nil", data, err, "hello world") + } +} + +func TestWriterReset(t *testing.T) { + buf := new(bytes.Buffer) + buf2 := new(bytes.Buffer) + z := NewWriter(buf) + msg := []byte("hello world") + z.Write(msg) + z.Close() + z.Reset(buf2) + z.Write(msg) + z.Close() + if buf.String() != buf2.String() { + t.Errorf("buf2 %q != original buf of %q", buf2.String(), buf.String()) + } +} diff --git a/libgo/go/compress/gzip/testdata/issue6550.gz b/libgo/go/compress/gzip/testdata/issue6550.gz Binary files differnew file mode 100644 index 0000000..57972b6 --- /dev/null +++ b/libgo/go/compress/gzip/testdata/issue6550.gz diff --git a/libgo/go/compress/zlib/writer.go b/libgo/go/compress/zlib/writer.go index cd8dea4..99ff654 100644 --- a/libgo/go/compress/zlib/writer.go +++ b/libgo/go/compress/zlib/writer.go @@ -70,6 +70,23 @@ func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*Writer, error) { }, nil } +// Reset clears the state of the Writer z such that it is equivalent to its +// initial state from NewWriterLevel or NewWriterLevelDict, but instead writing +// to w. +func (z *Writer) Reset(w io.Writer) { + z.w = w + // z.level and z.dict left unchanged. + if z.compressor != nil { + z.compressor.Reset(w) + } + if z.digest != nil { + z.digest.Reset() + } + z.err = nil + z.scratch = [4]byte{} + z.wroteHeader = false +} + // writeHeader writes the ZLIB header. func (z *Writer) writeHeader() (err error) { z.wroteHeader = true @@ -111,11 +128,15 @@ func (z *Writer) writeHeader() (err error) { return err } } - z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict) - if err != nil { - return err + if z.compressor == nil { + // Initialize deflater unless the Writer is being reused + // after a Reset call. + z.compressor, err = flate.NewWriterDict(z.w, z.level, z.dict) + if err != nil { + return err + } + z.digest = adler32.New() } - z.digest = adler32.New() return nil } diff --git a/libgo/go/compress/zlib/writer_test.go b/libgo/go/compress/zlib/writer_test.go index aee1a5c..cf9c832 100644 --- a/libgo/go/compress/zlib/writer_test.go +++ b/libgo/go/compress/zlib/writer_test.go @@ -89,6 +89,56 @@ func testLevelDict(t *testing.T, fn string, b0 []byte, level int, d string) { } } +func testFileLevelDictReset(t *testing.T, fn string, level int, dict []byte) { + var b0 []byte + var err error + if fn != "" { + b0, err = ioutil.ReadFile(fn) + if err != nil { + t.Errorf("%s (level=%d): %v", fn, level, err) + return + } + } + + // Compress once. + buf := new(bytes.Buffer) + var zlibw *Writer + if dict == nil { + zlibw, err = NewWriterLevel(buf, level) + } else { + zlibw, err = NewWriterLevelDict(buf, level, dict) + } + if err == nil { + _, err = zlibw.Write(b0) + } + if err == nil { + err = zlibw.Close() + } + if err != nil { + t.Errorf("%s (level=%d): %v", fn, level, err) + return + } + out := buf.String() + + // Reset and comprses again. + buf2 := new(bytes.Buffer) + zlibw.Reset(buf2) + _, err = zlibw.Write(b0) + if err == nil { + err = zlibw.Close() + } + if err != nil { + t.Errorf("%s (level=%d): %v", fn, level, err) + return + } + out2 := buf2.String() + + if out2 != out { + t.Errorf("%s (level=%d): different output after reset (got %d bytes, expected %d", + fn, level, len(out2), len(out)) + } +} + func TestWriter(t *testing.T) { for i, s := range data { b := []byte(s) @@ -122,6 +172,21 @@ func TestWriterDict(t *testing.T) { } } +func TestWriterReset(t *testing.T) { + const dictionary = "0123456789." + for _, fn := range filenames { + testFileLevelDictReset(t, fn, NoCompression, nil) + testFileLevelDictReset(t, fn, DefaultCompression, nil) + testFileLevelDictReset(t, fn, NoCompression, []byte(dictionary)) + testFileLevelDictReset(t, fn, DefaultCompression, []byte(dictionary)) + if !testing.Short() { + for level := BestSpeed; level <= BestCompression; level++ { + testFileLevelDictReset(t, fn, level, nil) + } + } + } +} + func TestWriterDictIsUsed(t *testing.T) { var input = []byte("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") var buf bytes.Buffer diff --git a/libgo/go/container/heap/heap.go b/libgo/go/container/heap/heap.go index c37e50e..52c8507 100644 --- a/libgo/go/container/heap/heap.go +++ b/libgo/go/container/heap/heap.go @@ -6,6 +6,8 @@ // heap.Interface. A heap is a tree with the property that each node is the // minimum-valued node in its subtree. // +// The minimum element in the tree is the root, at index 0. +// // A heap is a common way to implement a priority queue. To build a priority // queue, implement the Heap interface with the (negative) priority as the // ordering for the Less method, so Push adds items while Pop removes the @@ -54,7 +56,7 @@ func Push(h Interface, x interface{}) { // Pop removes the minimum element (according to Less) from the heap // and returns it. The complexity is O(log(n)) where n = h.Len(). -// Same as Remove(h, 0). +// It is equivalent to Remove(h, 0). // func Pop(h Interface) interface{} { n := h.Len() - 1 @@ -76,6 +78,15 @@ 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. +// 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(). +func Fix(h Interface, i int) { + down(h, i, h.Len()) + up(h, i) +} + func up(h Interface, j int) { for { i := (j - 1) / 2 // parent diff --git a/libgo/go/container/heap/heap_test.go b/libgo/go/container/heap/heap_test.go index 274d587..b3d054c 100644 --- a/libgo/go/container/heap/heap_test.go +++ b/libgo/go/container/heap/heap_test.go @@ -5,6 +5,7 @@ package heap import ( + "math/rand" "testing" ) @@ -182,3 +183,31 @@ func BenchmarkDup(b *testing.B) { } } } + +func TestFix(t *testing.T) { + h := new(myHeap) + h.verify(t, 0) + + for i := 200; i > 0; i -= 10 { + Push(h, i) + } + h.verify(t, 0) + + if (*h)[0] != 10 { + t.Fatalf("Expected head to be 10, was %d", (*h)[0]) + } + (*h)[0] = 210 + Fix(h, 0) + h.verify(t, 0) + + for i := 100; i > 0; i-- { + elem := rand.Intn(h.Len()) + if i&1 == 0 { + (*h)[elem] *= 2 + } else { + (*h)[elem] /= 2 + } + Fix(h, elem) + h.verify(t, 0) + } +} diff --git a/libgo/go/container/list/list.go b/libgo/go/container/list/list.go index 562a5ba..ed2d15a 100755 --- a/libgo/go/container/list/list.go +++ b/libgo/go/container/list/list.go @@ -29,7 +29,7 @@ type Element struct { // Next returns the next list element or nil. func (e *Element) Next() *Element { - if p := e.next; p != &e.list.root { + if p := e.next; e.list != nil && p != &e.list.root { return p } return nil @@ -37,7 +37,7 @@ func (e *Element) Next() *Element { // Prev returns the previous list element or nil. func (e *Element) Prev() *Element { - if p := e.prev; p != &e.list.root { + if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil @@ -62,6 +62,7 @@ func (l *List) Init() *List { func New() *List { return new(List).Init() } // Len returns the number of elements of list l. +// The complexity is O(1). func (l *List) Len() int { return l.len } // Front returns the first element of list l or nil @@ -126,7 +127,7 @@ func (l *List) Remove(e *Element) interface{} { return e.Value } -// Pushfront inserts a new element e with value v at the front of list l and returns e. +// PushFront inserts a new element e with value v at the front of list l and returns e. func (l *List) PushFront(v interface{}) *Element { l.lazyInit() return l.insertValue(v, &l.root) @@ -178,6 +179,24 @@ func (l *List) MoveToBack(e *Element) { l.insert(l.remove(e), l.root.prev) } +// MoveBefore moves element e to its new position before mark. +// If e is not an element of l, or e == mark, the list is not modified. +func (l *List) MoveBefore(e, mark *Element) { + if e.list != l || e == mark { + return + } + l.insert(l.remove(e), mark.prev) +} + +// MoveAfter moves element e to its new position after mark. +// If e is not an element of l, or e == mark, the list is not modified. +func (l *List) MoveAfter(e, mark *Element) { + if e.list != l || e == mark { + return + } + l.insert(l.remove(e), mark) +} + // PushBackList inserts a copy of an other list at the back of list l. // The lists l and other may be the same. func (l *List) PushBackList(other *List) { diff --git a/libgo/go/container/list/list_test.go b/libgo/go/container/list/list_test.go index b4fc77d..ee52afe 100755 --- a/libgo/go/container/list/list_test.go +++ b/libgo/go/container/list/list_test.go @@ -233,3 +233,55 @@ func TestIssue4103(t *testing.T) { t.Errorf("l1.Len() = %d, want 3", n) } } + +func TestIssue6349(t *testing.T) { + l := New() + l.PushBack(1) + l.PushBack(2) + + e := l.Front() + l.Remove(e) + if e.Value != 1 { + t.Errorf("e.value = %d, want 1", e.Value) + } + if e.Next() != nil { + t.Errorf("e.Next() != nil") + } + if e.Prev() != nil { + t.Errorf("e.Prev() != nil") + } +} + +func TestMove(t *testing.T) { + l := New() + e1 := l.PushBack(1) + e2 := l.PushBack(2) + e3 := l.PushBack(3) + e4 := l.PushBack(4) + + l.MoveAfter(e3, e3) + checkListPointers(t, l, []*Element{e1, e2, e3, e4}) + l.MoveBefore(e2, e2) + checkListPointers(t, l, []*Element{e1, e2, e3, e4}) + + l.MoveAfter(e3, e2) + checkListPointers(t, l, []*Element{e1, e2, e3, e4}) + l.MoveBefore(e2, e3) + checkListPointers(t, l, []*Element{e1, e2, e3, e4}) + + l.MoveBefore(e2, e4) + checkListPointers(t, l, []*Element{e1, e3, e2, e4}) + e1, e2, e3, e4 = e1, e3, e2, e4 + + l.MoveBefore(e4, e1) + checkListPointers(t, l, []*Element{e4, e1, e2, e3}) + e1, e2, e3, e4 = e4, e1, e2, e3 + + l.MoveAfter(e4, e1) + checkListPointers(t, l, []*Element{e1, e4, e2, e3}) + e1, e2, e3, e4 = e1, e4, e2, e3 + + l.MoveAfter(e2, e3) + checkListPointers(t, l, []*Element{e1, e3, e2, e4}) + e1, e2, e3, e4 = e1, e3, e2, e4 +} diff --git a/libgo/go/crypto/cipher/cbc.go b/libgo/go/crypto/cipher/cbc.go index 913a564..4189677 100644 --- a/libgo/go/crypto/cipher/cbc.go +++ b/libgo/go/crypto/cipher/cbc.go @@ -61,6 +61,13 @@ func (x *cbcEncrypter) CryptBlocks(dst, src []byte) { } } +func (x *cbcEncrypter) SetIV(iv []byte) { + if len(iv) != len(x.iv) { + panic("cipher: incorrect length IV") + } + copy(x.iv, iv) +} + type cbcDecrypter cbc // NewCBCDecrypter returns a BlockMode which decrypts in cipher block chaining @@ -94,3 +101,10 @@ func (x *cbcDecrypter) CryptBlocks(dst, src []byte) { dst = dst[x.blockSize:] } } + +func (x *cbcDecrypter) SetIV(iv []byte) { + if len(iv) != len(x.iv) { + panic("cipher: incorrect length IV") + } + copy(x.iv, iv) +} diff --git a/libgo/go/crypto/cipher/gcm.go b/libgo/go/crypto/cipher/gcm.go new file mode 100644 index 0000000..2bcb469 --- /dev/null +++ b/libgo/go/crypto/cipher/gcm.go @@ -0,0 +1,350 @@ +// 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 cipher + +import ( + "crypto/subtle" + "errors" +) + +// AEAD is a cipher mode providing authenticated encryption with associated +// data. +type AEAD interface { + // NonceSize returns the size of the nonce that must be passed to Seal + // and Open. + NonceSize() int + + // Overhead returns the maximum difference between the lengths of a + // plaintext and ciphertext. + Overhead() int + + // Seal encrypts and authenticates plaintext, authenticates the + // additional data and appends the result to dst, returning the updated + // slice. The nonce must be NonceSize() bytes long and unique for all + // time, for a given key. + // + // The plaintext and dst may alias exactly or not at all. + Seal(dst, nonce, plaintext, data []byte) []byte + + // 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. + // + // The ciphertext and dst may alias exactly or not at all. + Open(dst, nonce, ciphertext, data []byte) ([]byte, error) +} + +// gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM +// standard and make getUint64 suitable for marshaling these values, the bits +// are stored backwards. For example: +// the coefficient of x⁰ can be obtained by v.low >> 63. +// the coefficient of x⁶³ can be obtained by v.low & 1. +// the coefficient of x⁶⁴ can be obtained by v.high >> 63. +// the coefficient of x¹²⁷ can be obtained by v.high & 1. +type gcmFieldElement struct { + low, high uint64 +} + +// gcm represents a Galois Counter Mode with a specific key. See +// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf +type gcm struct { + cipher Block + // productTable contains the first sixteen powers of the key, H. + // However, they are in bit reversed order. See NewGCM. + productTable [16]gcmFieldElement +} + +// NewGCM returns the given 128-bit, block cipher wrapped in Galois Counter Mode. +func NewGCM(cipher Block) (AEAD, error) { + if cipher.BlockSize() != gcmBlockSize { + return nil, errors.New("cipher: NewGCM requires 128-bit block cipher") + } + + var key [gcmBlockSize]byte + cipher.Encrypt(key[:], key[:]) + + g := &gcm{cipher: cipher} + + // We precompute 16 multiples of |key|. However, when we do lookups + // into this table we'll be using bits from a field element and + // therefore the bits will be in the reverse order. So normally one + // would expect, say, 4*key to be in index 4 of the table but due to + // this bit ordering it will actually be in index 0010 (base 2) = 2. + x := gcmFieldElement{ + getUint64(key[:8]), + getUint64(key[8:]), + } + g.productTable[reverseBits(1)] = x + + for i := 2; i < 16; i += 2 { + g.productTable[reverseBits(i)] = gcmDouble(&g.productTable[reverseBits(i/2)]) + g.productTable[reverseBits(i+1)] = gcmAdd(&g.productTable[reverseBits(i)], &x) + } + + return g, nil +} + +const ( + gcmBlockSize = 16 + gcmTagSize = 16 + gcmNonceSize = 12 +) + +func (*gcm) NonceSize() int { + return gcmNonceSize +} + +func (*gcm) Overhead() int { + return gcmTagSize +} + +func (g *gcm) Seal(dst, nonce, plaintext, data []byte) []byte { + if len(nonce) != gcmNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + + ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize) + + // See GCM spec, section 7.1. + var counter, tagMask [gcmBlockSize]byte + copy(counter[:], nonce) + counter[gcmBlockSize-1] = 1 + + g.cipher.Encrypt(tagMask[:], counter[:]) + gcmInc32(&counter) + + g.counterCrypt(out, plaintext, &counter) + g.auth(out[len(plaintext):], out[:len(plaintext)], data, &tagMask) + + return ret +} + +var errOpen = errors.New("cipher: message authentication failed") + +func (g *gcm) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { + if len(nonce) != gcmNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + + if len(ciphertext) < gcmTagSize { + return nil, errOpen + } + tag := ciphertext[len(ciphertext)-gcmTagSize:] + ciphertext = ciphertext[:len(ciphertext)-gcmTagSize] + + // See GCM spec, section 7.1. + var counter, tagMask [gcmBlockSize]byte + copy(counter[:], nonce) + counter[gcmBlockSize-1] = 1 + + g.cipher.Encrypt(tagMask[:], counter[:]) + gcmInc32(&counter) + + var expectedTag [gcmTagSize]byte + g.auth(expectedTag[:], ciphertext, data, &tagMask) + + if subtle.ConstantTimeCompare(expectedTag[:], tag) != 1 { + return nil, errOpen + } + + ret, out := sliceForAppend(dst, len(ciphertext)) + g.counterCrypt(out, ciphertext, &counter) + + return ret, nil +} + +// reverseBits reverses the order of the bits of 4-bit number in i. +func reverseBits(i int) int { + i = ((i << 2) & 0xc) | ((i >> 2) & 0x3) + i = ((i << 1) & 0xa) | ((i >> 1) & 0x5) + return i +} + +// gcmAdd adds two elements of GF(2¹²⁸) and returns the sum. +func gcmAdd(x, y *gcmFieldElement) gcmFieldElement { + // Addition in a characteristic 2 field is just XOR. + return gcmFieldElement{x.low ^ y.low, x.high ^ y.high} +} + +// gcmDouble returns the result of doubling an element of GF(2¹²⁸). +func gcmDouble(x *gcmFieldElement) (double gcmFieldElement) { + msbSet := x.high&1 == 1 + + // Because of the bit-ordering, doubling is actually a right shift. + double.high = x.high >> 1 + double.high |= x.low << 63 + double.low = x.low >> 1 + + // If the most-significant bit was set before shifting then it, + // conceptually, becomes a term of x^128. This is greater than the + // irreducible polynomial so the result has to be reduced. The + // irreducible polynomial is 1+x+x^2+x^7+x^128. We can subtract that to + // eliminate the term at x^128 which also means subtracting the other + // four terms. In characteristic 2 fields, subtraction == addition == + // XOR. + if msbSet { + double.low ^= 0xe100000000000000 + } + + return +} + +var gcmReductionTable = []uint16{ + 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, + 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0, +} + +// mul sets y to y*H, where H is the GCM key, fixed during NewGCM. +func (g *gcm) mul(y *gcmFieldElement) { + var z gcmFieldElement + + for i := 0; i < 2; i++ { + word := y.high + if i == 1 { + word = y.low + } + + // Multiplication works by multiplying z by 16 and adding in + // one of the precomputed multiples of H. + for j := 0; j < 64; j += 4 { + msw := z.high & 0xf + z.high >>= 4 + z.high |= z.low << 60 + z.low >>= 4 + z.low ^= uint64(gcmReductionTable[msw]) << 48 + + // the values in |table| are ordered for + // little-endian bit positions. See the comment + // in NewGCM. + t := &g.productTable[word&0xf] + + z.low ^= t.low + z.high ^= t.high + word >>= 4 + } + } + + *y = z +} + +// updateBlocks extends y with more polynomial terms from blocks, based on +// Horner's rule. There must be a multiple of gcmBlockSize bytes in blocks. +func (g *gcm) updateBlocks(y *gcmFieldElement, blocks []byte) { + for len(blocks) > 0 { + y.low ^= getUint64(blocks) + y.high ^= getUint64(blocks[8:]) + g.mul(y) + blocks = blocks[gcmBlockSize:] + } +} + +// update extends y with more polynomial terms from data. If data is not a +// multiple of gcmBlockSize bytes long then the remainder is zero padded. +func (g *gcm) update(y *gcmFieldElement, data []byte) { + fullBlocks := (len(data) >> 4) << 4 + g.updateBlocks(y, data[:fullBlocks]) + + if len(data) != fullBlocks { + var partialBlock [gcmBlockSize]byte + copy(partialBlock[:], data[fullBlocks:]) + g.updateBlocks(y, partialBlock[:]) + } +} + +// gcmInc32 treats the final four bytes of counterBlock as a big-endian value +// and increments it. +func gcmInc32(counterBlock *[16]byte) { + c := 1 + for i := gcmBlockSize - 1; i >= gcmBlockSize-4; i-- { + c += int(counterBlock[i]) + counterBlock[i] = byte(c) + c >>= 8 + } +} + +// sliceForAppend takes a slice and a requested number of bytes. It returns a +// slice with the contents of the given slice followed by that many bytes and a +// second slice that aliases into it and contains only the extra bytes. If the +// original slice has sufficient capacity then no allocation is performed. +func sliceForAppend(in []byte, n int) (head, tail []byte) { + if total := len(in) + n; cap(in) >= total { + head = in[:total] + } else { + head = make([]byte, total) + copy(head, in) + } + tail = head[len(in):] + return +} + +// counterCrypt crypts in to out using g.cipher in counter mode. +func (g *gcm) counterCrypt(out, in []byte, counter *[gcmBlockSize]byte) { + var mask [gcmBlockSize]byte + + for len(in) >= gcmBlockSize { + g.cipher.Encrypt(mask[:], counter[:]) + gcmInc32(counter) + + for i := range mask { + out[i] = in[i] ^ mask[i] + } + out = out[gcmBlockSize:] + in = in[gcmBlockSize:] + } + + if len(in) > 0 { + g.cipher.Encrypt(mask[:], counter[:]) + gcmInc32(counter) + + for i := range in { + out[i] = in[i] ^ mask[i] + } + } +} + +// auth calculates GHASH(ciphertext, additionalData), masks the result with +// tagMask and writes the result to out. +func (g *gcm) auth(out, ciphertext, additionalData []byte, tagMask *[gcmTagSize]byte) { + var y gcmFieldElement + g.update(&y, additionalData) + g.update(&y, ciphertext) + + y.low ^= uint64(len(additionalData)) * 8 + y.high ^= uint64(len(ciphertext)) * 8 + + g.mul(&y) + + putUint64(out, y.low) + putUint64(out[8:], y.high) + + for i := range tagMask { + out[i] ^= tagMask[i] + } +} + +func getUint64(data []byte) uint64 { + r := uint64(data[0])<<56 | + uint64(data[1])<<48 | + uint64(data[2])<<40 | + uint64(data[3])<<32 | + uint64(data[4])<<24 | + uint64(data[5])<<16 | + uint64(data[6])<<8 | + uint64(data[7]) + return r +} + +func putUint64(out []byte, v uint64) { + out[0] = byte(v >> 56) + out[1] = byte(v >> 48) + out[2] = byte(v >> 40) + out[3] = byte(v >> 32) + out[4] = byte(v >> 24) + out[5] = byte(v >> 16) + out[6] = byte(v >> 8) + out[7] = byte(v) +} diff --git a/libgo/go/crypto/cipher/gcm_test.go b/libgo/go/crypto/cipher/gcm_test.go new file mode 100644 index 0000000..02d4215 --- /dev/null +++ b/libgo/go/crypto/cipher/gcm_test.go @@ -0,0 +1,175 @@ +// 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 cipher_test + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "encoding/hex" + "testing" +) + +// AES-GCM test vectors taken from gcmEncryptExtIV128.rsp from +// http://csrc.nist.gov/groups/STM/cavp/index.html. +var aesGCMTests = []struct { + key, nonce, plaintext, ad, result string +}{ + { + "11754cd72aec309bf52f7687212e8957", + "3c819d9a9bed087615030b65", + "", + "", + "250327c674aaf477aef2675748cf6971", + }, + { + "ca47248ac0b6f8372a97ac43508308ed", + "ffd2b598feabc9019262d2be", + "", + "", + "60d20404af527d248d893ae495707d1a", + }, + { + "77be63708971c4e240d1cb79e8d77feb", + "e0e00f19fed7ba0136a797f3", + "", + "7a43ec1d9c0a5a78a0b16533a6213cab", + "209fcc8d3675ed938e9c7166709dd946", + }, + { + "7680c5d3ca6154758e510f4d25b98820", + "f8f105f9c3df4965780321f8", + "", + "c94c410194c765e3dcc7964379758ed3", + "94dca8edfcf90bb74b153c8d48a17930", + }, + { + "7fddb57453c241d03efbed3ac44e371c", + "ee283a3fc75575e33efd4887", + "d5de42b461646c255c87bd2962d3b9a2", + "", + "2ccda4a5415cb91e135c2a0f78c9b2fdb36d1df9b9d5e596f83e8b7f52971cb3", + }, + { + "ab72c77b97cb5fe9a382d9fe81ffdbed", + "54cc7dc2c37ec006bcc6d1da", + "007c5e5b3e59df24a7c355584fc1518d", + "", + "0e1bde206a07a9c2c1b65300f8c649972b4401346697138c7a4891ee59867d0c", + }, + { + "fe47fcce5fc32665d2ae399e4eec72ba", + "5adb9609dbaeb58cbd6e7275", + "7c0e88c88899a779228465074797cd4c2e1498d259b54390b85e3eef1c02df60e743f1b840382c4bccaf3bafb4ca8429bea063", + "88319d6e1d3ffa5f987199166c8a9b56c2aeba5a", + "98f4826f05a265e6dd2be82db241c0fbbbf9ffb1c173aa83964b7cf5393043736365253ddbc5db8778371495da76d269e5db3e291ef1982e4defedaa2249f898556b47", + }, + { + "ec0c2ba17aa95cd6afffe949da9cc3a8", + "296bce5b50b7d66096d627ef", + "b85b3753535b825cbe5f632c0b843c741351f18aa484281aebec2f45bb9eea2d79d987b764b9611f6c0f8641843d5d58f3a242", + "f8d00f05d22bf68599bcdeb131292ad6e2df5d14", + "a7443d31c26bdf2a1c945e29ee4bd344a99cfaf3aa71f8b3f191f83c2adfc7a07162995506fde6309ffc19e716eddf1a828c5a890147971946b627c40016da1ecf3e77", + }, + { + "2c1f21cf0f6fb3661943155c3e3d8492", + "23cb5ff362e22426984d1907", + "42f758836986954db44bf37c6ef5e4ac0adaf38f27252a1b82d02ea949c8a1a2dbc0d68b5615ba7c1220ff6510e259f06655d8", + "5d3624879d35e46849953e45a32a624d6a6c536ed9857c613b572b0333e701557a713e3f010ecdf9a6bd6c9e3e44b065208645aff4aabee611b391528514170084ccf587177f4488f33cfb5e979e42b6e1cfc0a60238982a7aec", + "81824f0e0d523db30d3da369fdc0d60894c7a0a20646dd015073ad2732bd989b14a222b6ad57af43e1895df9dca2a5344a62cc57a3ee28136e94c74838997ae9823f3a", + }, + { + "d9f7d2411091f947b4d6f1e2d1f0fb2e", + "e1934f5db57cc983e6b180e7", + "73ed042327f70fe9c572a61545eda8b2a0c6e1d6c291ef19248e973aee6c312012f490c2c6f6166f4a59431e182663fcaea05a", + "0a8a18a7150e940c3d87b38e73baee9a5c049ee21795663e264b694a949822b639092d0e67015e86363583fcf0ca645af9f43375f05fdb4ce84f411dcbca73c2220dea03a20115d2e51398344b16bee1ed7c499b353d6c597af8", + "aaadbd5c92e9151ce3db7210b8714126b73e43436d242677afa50384f2149b831f1d573c7891c2a91fbc48db29967ec9542b2321b51ca862cb637cdd03b99a0f93b134", + }, + { + "fe9bb47deb3a61e423c2231841cfd1fb", + "4d328eb776f500a2f7fb47aa", + "f1cc3818e421876bb6b8bbd6c9", + "", + "b88c5c1977b35b517b0aeae96743fd4727fe5cdb4b5b42818dea7ef8c9", + }, + { + "6703df3701a7f54911ca72e24dca046a", + "12823ab601c350ea4bc2488c", + "793cd125b0b84a043e3ac67717", + "", + "b2051c80014f42f08735a7b0cd38e6bcd29962e5f2c13626b85a877101", + }, +} + +func TestAESGCM(t *testing.T) { + for i, test := range aesGCMTests { + key, _ := hex.DecodeString(test.key) + aes, err := aes.NewCipher(key) + if err != nil { + t.Fatal(err) + } + + nonce, _ := hex.DecodeString(test.nonce) + plaintext, _ := hex.DecodeString(test.plaintext) + ad, _ := hex.DecodeString(test.ad) + aesgcm, err := cipher.NewGCM(aes) + if err != nil { + t.Fatal(err) + } + + ct := aesgcm.Seal(nil, nonce, plaintext, ad) + if ctHex := hex.EncodeToString(ct); ctHex != test.result { + t.Errorf("#%d: got %s, want %s", i, ctHex, test.result) + continue + } + + plaintext2, err := aesgcm.Open(nil, nonce, ct, ad) + if err != nil { + t.Errorf("#%d: Open failed", i) + continue + } + + if !bytes.Equal(plaintext, plaintext2) { + t.Errorf("#%d: plaintext's don't match: got %x vs %x", i, plaintext2, plaintext) + continue + } + + if len(ad) > 0 { + ad[0] ^= 0x80 + if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil { + t.Errorf("#%d: Open was successful after altering additional data", i) + } + ad[0] ^= 0x80 + } + + nonce[0] ^= 0x80 + if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil { + t.Errorf("#%d: Open was successful after altering nonce", i) + } + nonce[0] ^= 0x80 + + ct[0] ^= 0x80 + if _, err := aesgcm.Open(nil, nonce, ct, ad); err == nil { + t.Errorf("#%d: Open was successful after altering ciphertext", i) + } + ct[0] ^= 0x80 + } +} + +func BenchmarkAESGCM(b *testing.B) { + buf := make([]byte, 1024) + b.SetBytes(int64(len(buf))) + + var key [16]byte + var nonce [12]byte + aes, _ := aes.NewCipher(key[:]) + aesgcm, _ := cipher.NewGCM(aes) + var out []byte + + b.ResetTimer() + for i := 0; i < b.N; i++ { + out = aesgcm.Seal(out[:0], nonce[:], buf, nonce[:]) + } +} diff --git a/libgo/go/crypto/cipher/io.go b/libgo/go/crypto/cipher/io.go index 807e8da..3938c0a 100644 --- a/libgo/go/crypto/cipher/io.go +++ b/libgo/go/crypto/cipher/io.go @@ -25,6 +25,8 @@ func (r StreamReader) Read(dst []byte) (n int, err error) { // StreamWriter wraps a Stream into an io.Writer. It calls XORKeyStream // to process each slice of data which passes through. If any Write call // returns short then the StreamWriter is out of sync and must be discarded. +// A StreamWriter has no internal buffering; Close does not need +// to be called to flush write data. type StreamWriter struct { S Stream W io.Writer @@ -43,8 +45,11 @@ func (w StreamWriter) Write(src []byte) (n int, err error) { return } +// Close closes the underlying Writer and returns its Close return value, if the Writer +// is also an io.Closer. Otherwise it returns nil. func (w StreamWriter) Close() error { - // This saves us from either requiring a WriteCloser or having a - // StreamWriterCloser. - return w.W.(io.Closer).Close() + if c, ok := w.W.(io.Closer); ok { + return c.Close() + } + return nil } diff --git a/libgo/go/crypto/crypto.go b/libgo/go/crypto/crypto.go index ecefc65..4b03628 100644 --- a/libgo/go/crypto/crypto.go +++ b/libgo/go/crypto/crypto.go @@ -7,6 +7,7 @@ package crypto import ( "hash" + "strconv" ) // Hash identifies a cryptographic hash function that is implemented in another @@ -59,7 +60,7 @@ func (h Hash) New() hash.Hash { return f() } } - panic("crypto: requested hash function is unavailable") + panic("crypto: requested hash function #" + strconv.Itoa(int(h)) + " is unavailable") } // Available reports whether the given hash function is linked into the binary. @@ -77,5 +78,8 @@ func RegisterHash(h Hash, f func() hash.Hash) { hashes[h] = f } +// PublicKey represents a public key using an unspecified algorithm. +type PublicKey interface{} + // PrivateKey represents a private key using an unspecified algorithm. type PrivateKey interface{} diff --git a/libgo/go/crypto/des/block.go b/libgo/go/crypto/des/block.go index c11c62c..26355a2 100644 --- a/libgo/go/crypto/des/block.go +++ b/libgo/go/crypto/des/block.go @@ -10,7 +10,7 @@ import ( func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) { b := binary.BigEndian.Uint64(src) - b = permuteBlock(b, initialPermutation[:]) + b = permuteInitialBlock(b) left, right := uint32(b>>32), uint32(b) var subkey uint64 @@ -25,7 +25,7 @@ func cryptBlock(subkeys []uint64, dst, src []byte, decrypt bool) { } // switch left & right and perform final permutation preOutput := (uint64(right) << 32) | uint64(left) - binary.BigEndian.PutUint64(dst, permuteBlock(preOutput, finalPermutation[:])) + binary.BigEndian.PutUint64(dst, permuteFinalBlock(preOutput)) } // Encrypt one block from src into dst, using the subkeys. @@ -40,20 +40,24 @@ func decryptBlock(subkeys []uint64, dst, src []byte) { // DES Feistel function func feistel(right uint32, key uint64) (result uint32) { - sBoxLocations := key ^ permuteBlock(uint64(right), expansionFunction[:]) + sBoxLocations := key ^ expandBlock(right) var sBoxResult uint32 for i := uint8(0); i < 8; i++ { sBoxLocation := uint8(sBoxLocations>>42) & 0x3f sBoxLocations <<= 6 // row determined by 1st and 6th bit - row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4) // column is middle four bits + row := (sBoxLocation & 0x1) | ((sBoxLocation & 0x20) >> 4) column := (sBoxLocation >> 1) & 0xf - sBoxResult |= uint32(sBoxes[i][row][column]) << (4 * (7 - i)) + sBoxResult ^= feistelBox[i][16*row+column] } - return uint32(permuteBlock(uint64(sBoxResult), permutationFunction[:])) + return sBoxResult } +// feistelBox[s][16*i+j] contains the output of permutationFunction +// for sBoxes[s][i][j] << 4*(7-s) +var feistelBox [8][64]uint32 + // general purpose function to perform DES block permutations func permuteBlock(src uint64, permutation []uint8) (block uint64) { for position, n := range permutation { @@ -63,6 +67,127 @@ func permuteBlock(src uint64, permutation []uint8) (block uint64) { return } +func init() { + for s := range sBoxes { + for i := 0; i < 4; i++ { + for j := 0; j < 16; j++ { + f := uint64(sBoxes[s][i][j]) << (4 * (7 - uint(s))) + f = permuteBlock(uint64(f), permutationFunction[:]) + feistelBox[s][16*i+j] = uint32(f) + } + } + } +} + +// expandBlock expands an input block of 32 bits, +// producing an output block of 48 bits. +func expandBlock(src uint32) (block uint64) { + // rotate the 5 highest bits to the right. + src = (src << 5) | (src >> 27) + for i := 0; i < 8; i++ { + block <<= 6 + // take the 6 bits on the right + block |= uint64(src) & (1<<6 - 1) + // advance by 4 bits. + src = (src << 4) | (src >> 28) + } + return +} + +// permuteInitialBlock is equivalent to the permutation defined +// by initialPermutation. +func permuteInitialBlock(block uint64) uint64 { + // block = b7 b6 b5 b4 b3 b2 b1 b0 (8 bytes) + b1 := block >> 48 + b2 := block << 48 + block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48 + + // block = b1 b0 b5 b4 b3 b2 b7 b6 + b1 = block >> 32 & 0xff00ff + b2 = (block & 0xff00ff00) + block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24 // exchange b0 b4 with b3 b7 + + // block is now b1 b3 b5 b7 b0 b2 b4 b7, the permutation: + // ... 8 + // ... 24 + // ... 40 + // ... 56 + // 7 6 5 4 3 2 1 0 + // 23 22 21 20 19 18 17 16 + // ... 32 + // ... 48 + + // exchange 4,5,6,7 with 32,33,34,35 etc. + b1 = block & 0x0f0f00000f0f0000 + b2 = block & 0x0000f0f00000f0f0 + block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12 + + // block is the permutation: + // + // [+8] [+40] + // + // 7 6 5 4 + // 23 22 21 20 + // 3 2 1 0 + // 19 18 17 16 [+32] + + // exchange 0,1,4,5 with 18,19,22,23 + b1 = block & 0x3300330033003300 + b2 = block & 0x00cc00cc00cc00cc + block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6 + + // block is the permutation: + // 15 14 + // 13 12 + // 11 10 + // 9 8 + // 7 6 + // 5 4 + // 3 2 + // 1 0 [+16] [+32] [+64] + + // exchange 0,2,4,6 with 9,11,13,15: + b1 = block & 0xaaaaaaaa55555555 + block ^= b1 ^ b1>>33 ^ b1<<33 + + // block is the permutation: + // 6 14 22 30 38 46 54 62 + // 4 12 20 28 36 44 52 60 + // 2 10 18 26 34 42 50 58 + // 0 8 16 24 32 40 48 56 + // 7 15 23 31 39 47 55 63 + // 5 13 21 29 37 45 53 61 + // 3 11 19 27 35 43 51 59 + // 1 9 17 25 33 41 49 57 + return block +} + +// permuteInitialBlock is equivalent to the permutation defined +// by finalPermutation. +func permuteFinalBlock(block uint64) uint64 { + // Perform the same bit exchanges as permuteInitialBlock + // but in reverse order. + b1 := block & 0xaaaaaaaa55555555 + block ^= b1 ^ b1>>33 ^ b1<<33 + + b1 = block & 0x3300330033003300 + b2 := block & 0x00cc00cc00cc00cc + block ^= b1 ^ b2 ^ b1>>6 ^ b2<<6 + + b1 = block & 0x0f0f00000f0f0000 + b2 = block & 0x0000f0f00000f0f0 + block ^= b1 ^ b2 ^ b1>>12 ^ b2<<12 + + b1 = block >> 32 & 0xff00ff + b2 = (block & 0xff00ff00) + block ^= b1<<32 ^ b2 ^ b1<<8 ^ b2<<24 + + b1 = block >> 48 + b2 = block << 48 + block ^= b1 ^ b2 ^ b1<<48 ^ b2>>48 + return block +} + // creates 16 28-bit blocks rotated according // to the rotation schedule func ksRotate(in uint32) (out []uint32) { diff --git a/libgo/go/crypto/des/des_test.go b/libgo/go/crypto/des/des_test.go index 2e87e99..2bd525a 100644 --- a/libgo/go/crypto/des/des_test.go +++ b/libgo/go/crypto/des/des_test.go @@ -1504,20 +1504,63 @@ func TestSubstitutionTableKnownAnswerDecrypt(t *testing.T) { } } -func ExampleNewTripleDESCipher() { - // NewTripleDESCipher can also be used when EDE2 is required by - // duplicating the first 8 bytes of the 16-byte key. - ede2Key := []byte("example key 1234") +func TestInitialPermute(t *testing.T) { + for i := uint(0); i < 64; i++ { + bit := uint64(1) << i + got := permuteInitialBlock(bit) + want := uint64(1) << finalPermutation[63-i] + if got != want { + t.Errorf("permute(%x) = %x, want %x", bit, got, want) + } + } +} - var tripleDESKey []byte - tripleDESKey = append(tripleDESKey, ede2Key[:16]...) - tripleDESKey = append(tripleDESKey, ede2Key[:8]...) +func TestFinalPermute(t *testing.T) { + for i := uint(0); i < 64; i++ { + bit := uint64(1) << i + got := permuteFinalBlock(bit) + want := uint64(1) << initialPermutation[63-i] + if got != want { + t.Errorf("permute(%x) = %x, want %x", bit, got, want) + } + } +} - _, err := NewTripleDESCipher(tripleDESKey) +func TestExpandBlock(t *testing.T) { + for i := uint(0); i < 32; i++ { + bit := uint32(1) << i + got := expandBlock(bit) + want := permuteBlock(uint64(bit), expansionFunction[:]) + if got != want { + t.Errorf("expand(%x) = %x, want %x", bit, got, want) + } + } +} + +func BenchmarkEncrypt(b *testing.B) { + tt := encryptDESTests[0] + c, err := NewCipher(tt.key) if err != nil { - panic(err) + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.in)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Encrypt(out, tt.in) } +} - // See crypto/cipher for how to use a cipher.Block for encryption and - // decryption. +func BenchmarkDecrypt(b *testing.B) { + tt := encryptDESTests[0] + c, err := NewCipher(tt.key) + if err != nil { + b.Fatal("NewCipher:", err) + } + out := make([]byte, len(tt.out)) + b.SetBytes(int64(len(out))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + c.Decrypt(out, tt.out) + } } diff --git a/libgo/go/crypto/ecdsa/ecdsa.go b/libgo/go/crypto/ecdsa/ecdsa.go index 2550002..d02f15c 100644 --- a/libgo/go/crypto/ecdsa/ecdsa.go +++ b/libgo/go/crypto/ecdsa/ecdsa.go @@ -123,8 +123,8 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err return } -// Verify verifies the signature in r, s of hash using the public key, pub. It -// returns true iff the signature is valid. +// Verify verifies the signature in r, s of hash using the public key, pub. Its +// return value records whether the signature is valid. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { // See [NSA] 3.4.2 c := pub.Curve diff --git a/libgo/go/crypto/elliptic/elliptic.go b/libgo/go/crypto/elliptic/elliptic.go index 7a4ff66..ba673f8 100644 --- a/libgo/go/crypto/elliptic/elliptic.go +++ b/libgo/go/crypto/elliptic/elliptic.go @@ -322,7 +322,6 @@ func Unmarshal(curve Curve, data []byte) (x, y *big.Int) { } var initonce sync.Once -var p256 *CurveParams var p384 *CurveParams var p521 *CurveParams @@ -333,17 +332,6 @@ func initAll() { initP521() } -func initP256() { - // See FIPS 186-3, section D.2.3 - p256 = new(CurveParams) - p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10) - p256.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10) - p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16) - p256.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16) - p256.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16) - p256.BitSize = 256 -} - func initP384() { // See FIPS 186-3, section D.2.4 p384 = new(CurveParams) diff --git a/libgo/go/crypto/elliptic/elliptic_test.go b/libgo/go/crypto/elliptic/elliptic_test.go index 58f9039..4dc27c9 100644 --- a/libgo/go/crypto/elliptic/elliptic_test.go +++ b/libgo/go/crypto/elliptic/elliptic_test.go @@ -322,6 +322,52 @@ func TestGenericBaseMult(t *testing.T) { } } +func TestP256BaseMult(t *testing.T) { + p256 := P256() + p256Generic := p256.Params() + + scalars := make([]*big.Int, 0, len(p224BaseMultTests)+1) + for _, e := range p224BaseMultTests { + k, _ := new(big.Int).SetString(e.k, 10) + scalars = append(scalars, k) + } + k := new(big.Int).SetInt64(1) + k.Lsh(k, 500) + scalars = append(scalars, k) + + for i, k := range scalars { + x, y := p256.ScalarBaseMult(k.Bytes()) + x2, y2 := p256Generic.ScalarBaseMult(k.Bytes()) + if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 { + t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, x, y, x2, y2) + } + + if testing.Short() && i > 5 { + break + } + } +} + +func TestP256Mult(t *testing.T) { + p256 := P256() + p256Generic := p256.Params() + + for i, e := range p224BaseMultTests { + x, _ := new(big.Int).SetString(e.x, 16) + y, _ := new(big.Int).SetString(e.y, 16) + k, _ := new(big.Int).SetString(e.k, 10) + + xx, yy := p256.ScalarMult(x, y, k.Bytes()) + xx2, yy2 := p256Generic.ScalarMult(x, y, k.Bytes()) + if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 { + t.Errorf("#%d: got (%x, %x), want (%x, %x)", i, xx, yy, xx2, yy2) + } + if testing.Short() && i > 5 { + break + } + } +} + func TestInfinity(t *testing.T) { tests := []struct { name string @@ -371,6 +417,17 @@ func BenchmarkBaseMult(b *testing.B) { } } +func BenchmarkBaseMultP256(b *testing.B) { + b.ResetTimer() + p256 := P256() + e := p224BaseMultTests[25] + k, _ := new(big.Int).SetString(e.k, 10) + b.StartTimer() + for i := 0; i < b.N; i++ { + p256.ScalarBaseMult(k.Bytes()) + } +} + func TestMarshal(t *testing.T) { p224 := P224() _, x, y, err := GenerateKey(p224, rand.Reader) diff --git a/libgo/go/crypto/elliptic/p256.go b/libgo/go/crypto/elliptic/p256.go new file mode 100644 index 0000000..82be51e --- /dev/null +++ b/libgo/go/crypto/elliptic/p256.go @@ -0,0 +1,1186 @@ +// 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 elliptic + +// This file contains a constant-time, 32-bit implementation of P256. + +import ( + "math/big" +) + +type p256Curve struct { + *CurveParams +} + +var ( + p256 p256Curve + // RInverse contains 1/R mod p - the inverse of the Montgomery constant + // (2**257). + p256RInverse *big.Int +) + +func initP256() { + // See FIPS 186-3, section D.2.3 + p256.CurveParams = new(CurveParams) + p256.P, _ = new(big.Int).SetString("115792089210356248762697446949407573530086143415290314195533631308867097853951", 10) + p256.N, _ = new(big.Int).SetString("115792089210356248762697446949407573529996955224135760342422259061068512044369", 10) + p256.B, _ = new(big.Int).SetString("5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16) + p256.Gx, _ = new(big.Int).SetString("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16) + p256.Gy, _ = new(big.Int).SetString("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16) + p256.BitSize = 256 + + p256RInverse, _ = new(big.Int).SetString("7fffffff00000001fffffffe8000000100000000ffffffff0000000180000000", 16) +} + +func (curve p256Curve) Params() *CurveParams { + return curve.CurveParams +} + +// p256GetScalar endian-swaps the big-endian scalar value from in and writes it +// to out. If the scalar is equal or greater than the order of the group, it's +// reduced modulo that order. +func p256GetScalar(out *[32]byte, in []byte) { + n := new(big.Int).SetBytes(in) + var scalarBytes []byte + + if n.Cmp(p256.N) >= 0 { + n.Mod(n, p256.N) + scalarBytes = n.Bytes() + } else { + scalarBytes = in + } + + for i, v := range scalarBytes { + out[len(scalarBytes)-(1+i)] = v + } +} + +func (p256Curve) ScalarBaseMult(scalar []byte) (x, y *big.Int) { + var scalarReversed [32]byte + p256GetScalar(&scalarReversed, scalar) + + var x1, y1, z1 [p256Limbs]uint32 + p256ScalarBaseMult(&x1, &y1, &z1, &scalarReversed) + return p256ToAffine(&x1, &y1, &z1) +} + +func (p256Curve) ScalarMult(bigX, bigY *big.Int, scalar []byte) (x, y *big.Int) { + var scalarReversed [32]byte + p256GetScalar(&scalarReversed, scalar) + + var px, py, x1, y1, z1 [p256Limbs]uint32 + p256FromBig(&px, bigX) + p256FromBig(&py, bigY) + p256ScalarMult(&x1, &y1, &z1, &px, &py, &scalarReversed) + return p256ToAffine(&x1, &y1, &z1) +} + +// Field elements are represented as nine, unsigned 32-bit words. +// +// The value of an field element is: +// x[0] + (x[1] * 2**29) + (x[2] * 2**57) + ... + (x[8] * 2**228) +// +// That is, each limb is alternately 29 or 28-bits wide in little-endian +// order. +// +// This means that a field element hits 2**257, rather than 2**256 as we would +// like. A 28, 29, ... pattern would cause us to hit 2**256, but that causes +// problems when multiplying as terms end up one bit short of a limb which +// would require much bit-shifting to correct. +// +// Finally, the values stored in a field element are in Montgomery form. So the +// value |y| is stored as (y*R) mod p, where p is the P-256 prime and R is +// 2**257. + +const ( + p256Limbs = 9 + bottom29Bits = 0x1fffffff +) + +var ( + // p256One is the number 1 as a field element. + p256One = [p256Limbs]uint32{2, 0, 0, 0xffff800, 0x1fffffff, 0xfffffff, 0x1fbfffff, 0x1ffffff, 0} + p256Zero = [p256Limbs]uint32{0, 0, 0, 0, 0, 0, 0, 0, 0} + // p256P is the prime modulus as a field element. + p256P = [p256Limbs]uint32{0x1fffffff, 0xfffffff, 0x1fffffff, 0x3ff, 0, 0, 0x200000, 0xf000000, 0xfffffff} + // p2562P is the twice prime modulus as a field element. + p2562P = [p256Limbs]uint32{0x1ffffffe, 0xfffffff, 0x1fffffff, 0x7ff, 0, 0, 0x400000, 0xe000000, 0x1fffffff} +) + +// p256Precomputed contains precomputed values to aid the calculation of scalar +// multiples of the base point, G. It's actually two, equal length, tables +// concatenated. +// +// The first table contains (x,y) field element pairs for 16 multiples of the +// base point, G. +// +// Index | Index (binary) | Value +// 0 | 0000 | 0G (all zeros, omitted) +// 1 | 0001 | G +// 2 | 0010 | 2**64G +// 3 | 0011 | 2**64G + G +// 4 | 0100 | 2**128G +// 5 | 0101 | 2**128G + G +// 6 | 0110 | 2**128G + 2**64G +// 7 | 0111 | 2**128G + 2**64G + G +// 8 | 1000 | 2**192G +// 9 | 1001 | 2**192G + G +// 10 | 1010 | 2**192G + 2**64G +// 11 | 1011 | 2**192G + 2**64G + G +// 12 | 1100 | 2**192G + 2**128G +// 13 | 1101 | 2**192G + 2**128G + G +// 14 | 1110 | 2**192G + 2**128G + 2**64G +// 15 | 1111 | 2**192G + 2**128G + 2**64G + G +// +// The second table follows the same style, but the terms are 2**32G, +// 2**96G, 2**160G, 2**224G. +// +// This is ~2KB of data. +var p256Precomputed = [p256Limbs * 2 * 15 * 2]uint32{ + 0x11522878, 0xe730d41, 0xdb60179, 0x4afe2ff, 0x12883add, 0xcaddd88, 0x119e7edc, 0xd4a6eab, 0x3120bee, + 0x1d2aac15, 0xf25357c, 0x19e45cdd, 0x5c721d0, 0x1992c5a5, 0xa237487, 0x154ba21, 0x14b10bb, 0xae3fe3, + 0xd41a576, 0x922fc51, 0x234994f, 0x60b60d3, 0x164586ae, 0xce95f18, 0x1fe49073, 0x3fa36cc, 0x5ebcd2c, + 0xb402f2f, 0x15c70bf, 0x1561925c, 0x5a26704, 0xda91e90, 0xcdc1c7f, 0x1ea12446, 0xe1ade1e, 0xec91f22, + 0x26f7778, 0x566847e, 0xa0bec9e, 0x234f453, 0x1a31f21a, 0xd85e75c, 0x56c7109, 0xa267a00, 0xb57c050, + 0x98fb57, 0xaa837cc, 0x60c0792, 0xcfa5e19, 0x61bab9e, 0x589e39b, 0xa324c5, 0x7d6dee7, 0x2976e4b, + 0x1fc4124a, 0xa8c244b, 0x1ce86762, 0xcd61c7e, 0x1831c8e0, 0x75774e1, 0x1d96a5a9, 0x843a649, 0xc3ab0fa, + 0x6e2e7d5, 0x7673a2a, 0x178b65e8, 0x4003e9b, 0x1a1f11c2, 0x7816ea, 0xf643e11, 0x58c43df, 0xf423fc2, + 0x19633ffa, 0x891f2b2, 0x123c231c, 0x46add8c, 0x54700dd, 0x59e2b17, 0x172db40f, 0x83e277d, 0xb0dd609, + 0xfd1da12, 0x35c6e52, 0x19ede20c, 0xd19e0c0, 0x97d0f40, 0xb015b19, 0x449e3f5, 0xe10c9e, 0x33ab581, + 0x56a67ab, 0x577734d, 0x1dddc062, 0xc57b10d, 0x149b39d, 0x26a9e7b, 0xc35df9f, 0x48764cd, 0x76dbcca, + 0xca4b366, 0xe9303ab, 0x1a7480e7, 0x57e9e81, 0x1e13eb50, 0xf466cf3, 0x6f16b20, 0x4ba3173, 0xc168c33, + 0x15cb5439, 0x6a38e11, 0x73658bd, 0xb29564f, 0x3f6dc5b, 0x53b97e, 0x1322c4c0, 0x65dd7ff, 0x3a1e4f6, + 0x14e614aa, 0x9246317, 0x1bc83aca, 0xad97eed, 0xd38ce4a, 0xf82b006, 0x341f077, 0xa6add89, 0x4894acd, + 0x9f162d5, 0xf8410ef, 0x1b266a56, 0xd7f223, 0x3e0cb92, 0xe39b672, 0x6a2901a, 0x69a8556, 0x7e7c0, + 0x9b7d8d3, 0x309a80, 0x1ad05f7f, 0xc2fb5dd, 0xcbfd41d, 0x9ceb638, 0x1051825c, 0xda0cf5b, 0x812e881, + 0x6f35669, 0x6a56f2c, 0x1df8d184, 0x345820, 0x1477d477, 0x1645db1, 0xbe80c51, 0xc22be3e, 0xe35e65a, + 0x1aeb7aa0, 0xc375315, 0xf67bc99, 0x7fdd7b9, 0x191fc1be, 0x61235d, 0x2c184e9, 0x1c5a839, 0x47a1e26, + 0xb7cb456, 0x93e225d, 0x14f3c6ed, 0xccc1ac9, 0x17fe37f3, 0x4988989, 0x1a90c502, 0x2f32042, 0xa17769b, + 0xafd8c7c, 0x8191c6e, 0x1dcdb237, 0x16200c0, 0x107b32a1, 0x66c08db, 0x10d06a02, 0x3fc93, 0x5620023, + 0x16722b27, 0x68b5c59, 0x270fcfc, 0xfad0ecc, 0xe5de1c2, 0xeab466b, 0x2fc513c, 0x407f75c, 0xbaab133, + 0x9705fe9, 0xb88b8e7, 0x734c993, 0x1e1ff8f, 0x19156970, 0xabd0f00, 0x10469ea7, 0x3293ac0, 0xcdc98aa, + 0x1d843fd, 0xe14bfe8, 0x15be825f, 0x8b5212, 0xeb3fb67, 0x81cbd29, 0xbc62f16, 0x2b6fcc7, 0xf5a4e29, + 0x13560b66, 0xc0b6ac2, 0x51ae690, 0xd41e271, 0xf3e9bd4, 0x1d70aab, 0x1029f72, 0x73e1c35, 0xee70fbc, + 0xad81baf, 0x9ecc49a, 0x86c741e, 0xfe6be30, 0x176752e7, 0x23d416, 0x1f83de85, 0x27de188, 0x66f70b8, + 0x181cd51f, 0x96b6e4c, 0x188f2335, 0xa5df759, 0x17a77eb6, 0xfeb0e73, 0x154ae914, 0x2f3ec51, 0x3826b59, + 0xb91f17d, 0x1c72949, 0x1362bf0a, 0xe23fddf, 0xa5614b0, 0xf7d8f, 0x79061, 0x823d9d2, 0x8213f39, + 0x1128ae0b, 0xd095d05, 0xb85c0c2, 0x1ecb2ef, 0x24ddc84, 0xe35e901, 0x18411a4a, 0xf5ddc3d, 0x3786689, + 0x52260e8, 0x5ae3564, 0x542b10d, 0x8d93a45, 0x19952aa4, 0x996cc41, 0x1051a729, 0x4be3499, 0x52b23aa, + 0x109f307e, 0x6f5b6bb, 0x1f84e1e7, 0x77a0cfa, 0x10c4df3f, 0x25a02ea, 0xb048035, 0xe31de66, 0xc6ecaa3, + 0x28ea335, 0x2886024, 0x1372f020, 0xf55d35, 0x15e4684c, 0xf2a9e17, 0x1a4a7529, 0xcb7beb1, 0xb2a78a1, + 0x1ab21f1f, 0x6361ccf, 0x6c9179d, 0xb135627, 0x1267b974, 0x4408bad, 0x1cbff658, 0xe3d6511, 0xc7d76f, + 0x1cc7a69, 0xe7ee31b, 0x54fab4f, 0x2b914f, 0x1ad27a30, 0xcd3579e, 0xc50124c, 0x50daa90, 0xb13f72, + 0xb06aa75, 0x70f5cc6, 0x1649e5aa, 0x84a5312, 0x329043c, 0x41c4011, 0x13d32411, 0xb04a838, 0xd760d2d, + 0x1713b532, 0xbaa0c03, 0x84022ab, 0x6bcf5c1, 0x2f45379, 0x18ae070, 0x18c9e11e, 0x20bca9a, 0x66f496b, + 0x3eef294, 0x67500d2, 0xd7f613c, 0x2dbbeb, 0xb741038, 0xe04133f, 0x1582968d, 0xbe985f7, 0x1acbc1a, + 0x1a6a939f, 0x33e50f6, 0xd665ed4, 0xb4b7bd6, 0x1e5a3799, 0x6b33847, 0x17fa56ff, 0x65ef930, 0x21dc4a, + 0x2b37659, 0x450fe17, 0xb357b65, 0xdf5efac, 0x15397bef, 0x9d35a7f, 0x112ac15f, 0x624e62e, 0xa90ae2f, + 0x107eecd2, 0x1f69bbe, 0x77d6bce, 0x5741394, 0x13c684fc, 0x950c910, 0x725522b, 0xdc78583, 0x40eeabb, + 0x1fde328a, 0xbd61d96, 0xd28c387, 0x9e77d89, 0x12550c40, 0x759cb7d, 0x367ef34, 0xae2a960, 0x91b8bdc, + 0x93462a9, 0xf469ef, 0xb2e9aef, 0xd2ca771, 0x54e1f42, 0x7aaa49, 0x6316abb, 0x2413c8e, 0x5425bf9, + 0x1bed3e3a, 0xf272274, 0x1f5e7326, 0x6416517, 0xea27072, 0x9cedea7, 0x6e7633, 0x7c91952, 0xd806dce, + 0x8e2a7e1, 0xe421e1a, 0x418c9e1, 0x1dbc890, 0x1b395c36, 0xa1dc175, 0x1dc4ef73, 0x8956f34, 0xe4b5cf2, + 0x1b0d3a18, 0x3194a36, 0x6c2641f, 0xe44124c, 0xa2f4eaa, 0xa8c25ba, 0xf927ed7, 0x627b614, 0x7371cca, + 0xba16694, 0x417bc03, 0x7c0a7e3, 0x9c35c19, 0x1168a205, 0x8b6b00d, 0x10e3edc9, 0x9c19bf2, 0x5882229, + 0x1b2b4162, 0xa5cef1a, 0x1543622b, 0x9bd433e, 0x364e04d, 0x7480792, 0x5c9b5b3, 0xe85ff25, 0x408ef57, + 0x1814cfa4, 0x121b41b, 0xd248a0f, 0x3b05222, 0x39bb16a, 0xc75966d, 0xa038113, 0xa4a1769, 0x11fbc6c, + 0x917e50e, 0xeec3da8, 0x169d6eac, 0x10c1699, 0xa416153, 0xf724912, 0x15cd60b7, 0x4acbad9, 0x5efc5fa, + 0xf150ed7, 0x122b51, 0x1104b40a, 0xcb7f442, 0xfbb28ff, 0x6ac53ca, 0x196142cc, 0x7bf0fa9, 0x957651, + 0x4e0f215, 0xed439f8, 0x3f46bd5, 0x5ace82f, 0x110916b6, 0x6db078, 0xffd7d57, 0xf2ecaac, 0xca86dec, + 0x15d6b2da, 0x965ecc9, 0x1c92b4c2, 0x1f3811, 0x1cb080f5, 0x2d8b804, 0x19d1c12d, 0xf20bd46, 0x1951fa7, + 0xa3656c3, 0x523a425, 0xfcd0692, 0xd44ddc8, 0x131f0f5b, 0xaf80e4a, 0xcd9fc74, 0x99bb618, 0x2db944c, + 0xa673090, 0x1c210e1, 0x178c8d23, 0x1474383, 0x10b8743d, 0x985a55b, 0x2e74779, 0x576138, 0x9587927, + 0x133130fa, 0xbe05516, 0x9f4d619, 0xbb62570, 0x99ec591, 0xd9468fe, 0x1d07782d, 0xfc72e0b, 0x701b298, + 0x1863863b, 0x85954b8, 0x121a0c36, 0x9e7fedf, 0xf64b429, 0x9b9d71e, 0x14e2f5d8, 0xf858d3a, 0x942eea8, + 0xda5b765, 0x6edafff, 0xa9d18cc, 0xc65e4ba, 0x1c747e86, 0xe4ea915, 0x1981d7a1, 0x8395659, 0x52ed4e2, + 0x87d43b7, 0x37ab11b, 0x19d292ce, 0xf8d4692, 0x18c3053f, 0x8863e13, 0x4c146c0, 0x6bdf55a, 0x4e4457d, + 0x16152289, 0xac78ec2, 0x1a59c5a2, 0x2028b97, 0x71c2d01, 0x295851f, 0x404747b, 0x878558d, 0x7d29aa4, + 0x13d8341f, 0x8daefd7, 0x139c972d, 0x6b7ea75, 0xd4a9dde, 0xff163d8, 0x81d55d7, 0xa5bef68, 0xb7b30d8, + 0xbe73d6f, 0xaa88141, 0xd976c81, 0x7e7a9cc, 0x18beb771, 0xd773cbd, 0x13f51951, 0x9d0c177, 0x1c49a78, +} + +// Field element operations: + +// nonZeroToAllOnes returns: +// 0xffffffff for 0 < x <= 2**31 +// 0 for x == 0 or x > 2**31. +func nonZeroToAllOnes(x uint32) uint32 { + return ((x - 1) >> 31) - 1 +} + +// p256ReduceCarry adds a multiple of p in order to cancel |carry|, +// which is a term at 2**257. +// +// On entry: carry < 2**3, inout[0,2,...] < 2**29, inout[1,3,...] < 2**28. +// On exit: inout[0,2,..] < 2**30, inout[1,3,...] < 2**29. +func p256ReduceCarry(inout *[p256Limbs]uint32, carry uint32) { + carry_mask := nonZeroToAllOnes(carry) + + inout[0] += carry << 1 + inout[3] += 0x10000000 & carry_mask + // carry < 2**3 thus (carry << 11) < 2**14 and we added 2**28 in the + // previous line therefore this doesn't underflow. + inout[3] -= carry << 11 + inout[4] += (0x20000000 - 1) & carry_mask + inout[5] += (0x10000000 - 1) & carry_mask + inout[6] += (0x20000000 - 1) & carry_mask + inout[6] -= carry << 22 + // This may underflow if carry is non-zero but, if so, we'll fix it in the + // next line. + inout[7] -= 1 & carry_mask + inout[7] += carry << 25 +} + +// p256Sum sets out = in+in2. +// +// On entry, in[i]+in2[i] must not overflow a 32-bit word. +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 +func p256Sum(out, in, in2 *[p256Limbs]uint32) { + carry := uint32(0) + for i := 0; ; i++ { + out[i] = in[i] + in2[i] + out[i] += carry + carry = out[i] >> 29 + out[i] &= bottom29Bits + + i++ + if i == p256Limbs { + break + } + + out[i] = in[i] + in2[i] + out[i] += carry + carry = out[i] >> 28 + out[i] &= bottom28Bits + } + + p256ReduceCarry(out, carry) +} + +const ( + two30m2 = 1<<30 - 1<<2 + two30p13m2 = 1<<30 + 1<<13 - 1<<2 + two31m2 = 1<<31 - 1<<2 + two31p24m2 = 1<<31 + 1<<24 - 1<<2 + two30m27m2 = 1<<30 - 1<<27 - 1<<2 +) + +// p256Zero31 is 0 mod p. +var p256Zero31 = [p256Limbs]uint32{two31m3, two30m2, two31m2, two30p13m2, two31m2, two30m2, two31p24m2, two30m27m2, two31m2} + +// p256Diff sets out = in-in2. +// +// On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and +// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +func p256Diff(out, in, in2 *[p256Limbs]uint32) { + var carry uint32 + + for i := 0; ; i++ { + out[i] = in[i] - in2[i] + out[i] += p256Zero31[i] + out[i] += carry + carry = out[i] >> 29 + out[i] &= bottom29Bits + + i++ + if i == p256Limbs { + break + } + + out[i] = in[i] - in2[i] + out[i] += p256Zero31[i] + out[i] += carry + carry = out[i] >> 28 + out[i] &= bottom28Bits + } + + p256ReduceCarry(out, carry) +} + +// p256ReduceDegree sets out = tmp/R mod p where tmp contains 64-bit words with +// the same 29,28,... bit positions as an field element. +// +// The values in field elements are in Montgomery form: x*R mod p where R = +// 2**257. Since we just multiplied two Montgomery values together, the result +// is x*y*R*R mod p. We wish to divide by R in order for the result also to be +// in Montgomery form. +// +// On entry: tmp[i] < 2**64 +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29 +func p256ReduceDegree(out *[p256Limbs]uint32, tmp [17]uint64) { + // The following table may be helpful when reading this code: + // + // Limb number: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10... + // Width (bits): 29| 28| 29| 28| 29| 28| 29| 28| 29| 28| 29 + // Start bit: 0 | 29| 57| 86|114|143|171|200|228|257|285 + // (odd phase): 0 | 28| 57| 85|114|142|171|199|228|256|285 + var tmp2 [18]uint32 + var carry, x, xMask uint32 + + // tmp contains 64-bit words with the same 29,28,29-bit positions as an + // field element. So the top of an element of tmp might overlap with + // another element two positions down. The following loop eliminates + // this overlap. + tmp2[0] = uint32(tmp[0]) & bottom29Bits + + tmp2[1] = uint32(tmp[0]) >> 29 + tmp2[1] |= (uint32(tmp[0]>>32) << 3) & bottom28Bits + tmp2[1] += uint32(tmp[1]) & bottom28Bits + carry = tmp2[1] >> 28 + tmp2[1] &= bottom28Bits + + for i := 2; i < 17; i++ { + tmp2[i] = (uint32(tmp[i-2] >> 32)) >> 25 + tmp2[i] += (uint32(tmp[i-1])) >> 28 + tmp2[i] += (uint32(tmp[i-1]>>32) << 4) & bottom29Bits + tmp2[i] += uint32(tmp[i]) & bottom29Bits + tmp2[i] += carry + carry = tmp2[i] >> 29 + tmp2[i] &= bottom29Bits + + i++ + if i == 17 { + break + } + tmp2[i] = uint32(tmp[i-2]>>32) >> 25 + tmp2[i] += uint32(tmp[i-1]) >> 29 + tmp2[i] += ((uint32(tmp[i-1] >> 32)) << 3) & bottom28Bits + tmp2[i] += uint32(tmp[i]) & bottom28Bits + tmp2[i] += carry + carry = tmp2[i] >> 28 + tmp2[i] &= bottom28Bits + } + + tmp2[17] = uint32(tmp[15]>>32) >> 25 + tmp2[17] += uint32(tmp[16]) >> 29 + tmp2[17] += uint32(tmp[16]>>32) << 3 + tmp2[17] += carry + + // Montgomery elimination of terms: + // + // Since R is 2**257, we can divide by R with a bitwise shift if we can + // ensure that the right-most 257 bits are all zero. We can make that true + // by adding multiplies of p without affecting the value. + // + // So we eliminate limbs from right to left. Since the bottom 29 bits of p + // are all ones, then by adding tmp2[0]*p to tmp2 we'll make tmp2[0] == 0. + // We can do that for 8 further limbs and then right shift to eliminate the + // extra factor of R. + for i := 0; ; i += 2 { + tmp2[i+1] += tmp2[i] >> 29 + x = tmp2[i] & bottom29Bits + xMask = nonZeroToAllOnes(x) + tmp2[i] = 0 + + // The bounds calculations for this loop are tricky. Each iteration of + // the loop eliminates two words by adding values to words to their + // right. + // + // The following table contains the amounts added to each word (as an + // offset from the value of i at the top of the loop). The amounts are + // accounted for from the first and second half of the loop separately + // and are written as, for example, 28 to mean a value <2**28. + // + // Word: 3 4 5 6 7 8 9 10 + // Added in top half: 28 11 29 21 29 28 + // 28 29 + // 29 + // Added in bottom half: 29 10 28 21 28 28 + // 29 + // + // The value that is currently offset 7 will be offset 5 for the next + // iteration and then offset 3 for the iteration after that. Therefore + // the total value added will be the values added at 7, 5 and 3. + // + // The following table accumulates these values. The sums at the bottom + // are written as, for example, 29+28, to mean a value < 2**29+2**28. + // + // Word: 3 4 5 6 7 8 9 10 11 12 13 + // 28 11 10 29 21 29 28 28 28 28 28 + // 29 28 11 28 29 28 29 28 29 28 + // 29 28 21 21 29 21 29 21 + // 10 29 28 21 28 21 28 + // 28 29 28 29 28 29 28 + // 11 10 29 10 29 10 + // 29 28 11 28 11 + // 29 29 + // -------------------------------------------- + // 30+ 31+ 30+ 31+ 30+ + // 28+ 29+ 28+ 29+ 21+ + // 21+ 28+ 21+ 28+ 10 + // 10 21+ 10 21+ + // 11 11 + // + // So the greatest amount is added to tmp2[10] and tmp2[12]. If + // tmp2[10/12] has an initial value of <2**29, then the maximum value + // will be < 2**31 + 2**30 + 2**28 + 2**21 + 2**11, which is < 2**32, + // as required. + tmp2[i+3] += (x << 10) & bottom28Bits + tmp2[i+4] += (x >> 18) + + tmp2[i+6] += (x << 21) & bottom29Bits + tmp2[i+7] += x >> 8 + + // At position 200, which is the starting bit position for word 7, we + // have a factor of 0xf000000 = 2**28 - 2**24. + tmp2[i+7] += 0x10000000 & xMask + tmp2[i+8] += (x - 1) & xMask + tmp2[i+7] -= (x << 24) & bottom28Bits + tmp2[i+8] -= x >> 4 + + tmp2[i+8] += 0x20000000 & xMask + tmp2[i+8] -= x + tmp2[i+8] += (x << 28) & bottom29Bits + tmp2[i+9] += ((x >> 1) - 1) & xMask + + if i+1 == p256Limbs { + break + } + tmp2[i+2] += tmp2[i+1] >> 28 + x = tmp2[i+1] & bottom28Bits + xMask = nonZeroToAllOnes(x) + tmp2[i+1] = 0 + + tmp2[i+4] += (x << 11) & bottom29Bits + tmp2[i+5] += (x >> 18) + + tmp2[i+7] += (x << 21) & bottom28Bits + tmp2[i+8] += x >> 7 + + // At position 199, which is the starting bit of the 8th word when + // dealing with a context starting on an odd word, we have a factor of + // 0x1e000000 = 2**29 - 2**25. Since we have not updated i, the 8th + // word from i+1 is i+8. + tmp2[i+8] += 0x20000000 & xMask + tmp2[i+9] += (x - 1) & xMask + tmp2[i+8] -= (x << 25) & bottom29Bits + tmp2[i+9] -= x >> 4 + + tmp2[i+9] += 0x10000000 & xMask + tmp2[i+9] -= x + tmp2[i+10] += (x - 1) & xMask + } + + // We merge the right shift with a carry chain. The words above 2**257 have + // widths of 28,29,... which we need to correct when copying them down. + carry = 0 + for i := 0; i < 8; i++ { + // The maximum value of tmp2[i + 9] occurs on the first iteration and + // is < 2**30+2**29+2**28. Adding 2**29 (from tmp2[i + 10]) is + // therefore safe. + out[i] = tmp2[i+9] + out[i] += carry + out[i] += (tmp2[i+10] << 28) & bottom29Bits + carry = out[i] >> 29 + out[i] &= bottom29Bits + + i++ + out[i] = tmp2[i+9] >> 1 + out[i] += carry + carry = out[i] >> 28 + out[i] &= bottom28Bits + } + + out[8] = tmp2[17] + out[8] += carry + carry = out[8] >> 29 + out[8] &= bottom29Bits + + p256ReduceCarry(out, carry) +} + +// p256Square sets out=in*in. +// +// On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29. +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +func p256Square(out, in *[p256Limbs]uint32) { + var tmp [17]uint64 + + tmp[0] = uint64(in[0]) * uint64(in[0]) + tmp[1] = uint64(in[0]) * (uint64(in[1]) << 1) + tmp[2] = uint64(in[0])*(uint64(in[2])<<1) + + uint64(in[1])*(uint64(in[1])<<1) + tmp[3] = uint64(in[0])*(uint64(in[3])<<1) + + uint64(in[1])*(uint64(in[2])<<1) + tmp[4] = uint64(in[0])*(uint64(in[4])<<1) + + uint64(in[1])*(uint64(in[3])<<2) + + uint64(in[2])*uint64(in[2]) + tmp[5] = uint64(in[0])*(uint64(in[5])<<1) + + uint64(in[1])*(uint64(in[4])<<1) + + uint64(in[2])*(uint64(in[3])<<1) + tmp[6] = uint64(in[0])*(uint64(in[6])<<1) + + uint64(in[1])*(uint64(in[5])<<2) + + uint64(in[2])*(uint64(in[4])<<1) + + uint64(in[3])*(uint64(in[3])<<1) + tmp[7] = uint64(in[0])*(uint64(in[7])<<1) + + uint64(in[1])*(uint64(in[6])<<1) + + uint64(in[2])*(uint64(in[5])<<1) + + uint64(in[3])*(uint64(in[4])<<1) + // tmp[8] has the greatest value of 2**61 + 2**60 + 2**61 + 2**60 + 2**60, + // which is < 2**64 as required. + tmp[8] = uint64(in[0])*(uint64(in[8])<<1) + + uint64(in[1])*(uint64(in[7])<<2) + + uint64(in[2])*(uint64(in[6])<<1) + + uint64(in[3])*(uint64(in[5])<<2) + + uint64(in[4])*uint64(in[4]) + tmp[9] = uint64(in[1])*(uint64(in[8])<<1) + + uint64(in[2])*(uint64(in[7])<<1) + + uint64(in[3])*(uint64(in[6])<<1) + + uint64(in[4])*(uint64(in[5])<<1) + tmp[10] = uint64(in[2])*(uint64(in[8])<<1) + + uint64(in[3])*(uint64(in[7])<<2) + + uint64(in[4])*(uint64(in[6])<<1) + + uint64(in[5])*(uint64(in[5])<<1) + tmp[11] = uint64(in[3])*(uint64(in[8])<<1) + + uint64(in[4])*(uint64(in[7])<<1) + + uint64(in[5])*(uint64(in[6])<<1) + tmp[12] = uint64(in[4])*(uint64(in[8])<<1) + + uint64(in[5])*(uint64(in[7])<<2) + + uint64(in[6])*uint64(in[6]) + tmp[13] = uint64(in[5])*(uint64(in[8])<<1) + + uint64(in[6])*(uint64(in[7])<<1) + tmp[14] = uint64(in[6])*(uint64(in[8])<<1) + + uint64(in[7])*(uint64(in[7])<<1) + tmp[15] = uint64(in[7]) * (uint64(in[8]) << 1) + tmp[16] = uint64(in[8]) * uint64(in[8]) + + p256ReduceDegree(out, tmp) +} + +// p256Mul sets out=in*in2. +// +// On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and +// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29. +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +func p256Mul(out, in, in2 *[p256Limbs]uint32) { + var tmp [17]uint64 + + tmp[0] = uint64(in[0]) * uint64(in2[0]) + tmp[1] = uint64(in[0])*(uint64(in2[1])<<0) + + uint64(in[1])*(uint64(in2[0])<<0) + tmp[2] = uint64(in[0])*(uint64(in2[2])<<0) + + uint64(in[1])*(uint64(in2[1])<<1) + + uint64(in[2])*(uint64(in2[0])<<0) + tmp[3] = uint64(in[0])*(uint64(in2[3])<<0) + + uint64(in[1])*(uint64(in2[2])<<0) + + uint64(in[2])*(uint64(in2[1])<<0) + + uint64(in[3])*(uint64(in2[0])<<0) + tmp[4] = uint64(in[0])*(uint64(in2[4])<<0) + + uint64(in[1])*(uint64(in2[3])<<1) + + uint64(in[2])*(uint64(in2[2])<<0) + + uint64(in[3])*(uint64(in2[1])<<1) + + uint64(in[4])*(uint64(in2[0])<<0) + tmp[5] = uint64(in[0])*(uint64(in2[5])<<0) + + uint64(in[1])*(uint64(in2[4])<<0) + + uint64(in[2])*(uint64(in2[3])<<0) + + uint64(in[3])*(uint64(in2[2])<<0) + + uint64(in[4])*(uint64(in2[1])<<0) + + uint64(in[5])*(uint64(in2[0])<<0) + tmp[6] = uint64(in[0])*(uint64(in2[6])<<0) + + uint64(in[1])*(uint64(in2[5])<<1) + + uint64(in[2])*(uint64(in2[4])<<0) + + uint64(in[3])*(uint64(in2[3])<<1) + + uint64(in[4])*(uint64(in2[2])<<0) + + uint64(in[5])*(uint64(in2[1])<<1) + + uint64(in[6])*(uint64(in2[0])<<0) + tmp[7] = uint64(in[0])*(uint64(in2[7])<<0) + + uint64(in[1])*(uint64(in2[6])<<0) + + uint64(in[2])*(uint64(in2[5])<<0) + + uint64(in[3])*(uint64(in2[4])<<0) + + uint64(in[4])*(uint64(in2[3])<<0) + + uint64(in[5])*(uint64(in2[2])<<0) + + uint64(in[6])*(uint64(in2[1])<<0) + + uint64(in[7])*(uint64(in2[0])<<0) + // tmp[8] has the greatest value but doesn't overflow. See logic in + // p256Square. + tmp[8] = uint64(in[0])*(uint64(in2[8])<<0) + + uint64(in[1])*(uint64(in2[7])<<1) + + uint64(in[2])*(uint64(in2[6])<<0) + + uint64(in[3])*(uint64(in2[5])<<1) + + uint64(in[4])*(uint64(in2[4])<<0) + + uint64(in[5])*(uint64(in2[3])<<1) + + uint64(in[6])*(uint64(in2[2])<<0) + + uint64(in[7])*(uint64(in2[1])<<1) + + uint64(in[8])*(uint64(in2[0])<<0) + tmp[9] = uint64(in[1])*(uint64(in2[8])<<0) + + uint64(in[2])*(uint64(in2[7])<<0) + + uint64(in[3])*(uint64(in2[6])<<0) + + uint64(in[4])*(uint64(in2[5])<<0) + + uint64(in[5])*(uint64(in2[4])<<0) + + uint64(in[6])*(uint64(in2[3])<<0) + + uint64(in[7])*(uint64(in2[2])<<0) + + uint64(in[8])*(uint64(in2[1])<<0) + tmp[10] = uint64(in[2])*(uint64(in2[8])<<0) + + uint64(in[3])*(uint64(in2[7])<<1) + + uint64(in[4])*(uint64(in2[6])<<0) + + uint64(in[5])*(uint64(in2[5])<<1) + + uint64(in[6])*(uint64(in2[4])<<0) + + uint64(in[7])*(uint64(in2[3])<<1) + + uint64(in[8])*(uint64(in2[2])<<0) + tmp[11] = uint64(in[3])*(uint64(in2[8])<<0) + + uint64(in[4])*(uint64(in2[7])<<0) + + uint64(in[5])*(uint64(in2[6])<<0) + + uint64(in[6])*(uint64(in2[5])<<0) + + uint64(in[7])*(uint64(in2[4])<<0) + + uint64(in[8])*(uint64(in2[3])<<0) + tmp[12] = uint64(in[4])*(uint64(in2[8])<<0) + + uint64(in[5])*(uint64(in2[7])<<1) + + uint64(in[6])*(uint64(in2[6])<<0) + + uint64(in[7])*(uint64(in2[5])<<1) + + uint64(in[8])*(uint64(in2[4])<<0) + tmp[13] = uint64(in[5])*(uint64(in2[8])<<0) + + uint64(in[6])*(uint64(in2[7])<<0) + + uint64(in[7])*(uint64(in2[6])<<0) + + uint64(in[8])*(uint64(in2[5])<<0) + tmp[14] = uint64(in[6])*(uint64(in2[8])<<0) + + uint64(in[7])*(uint64(in2[7])<<1) + + uint64(in[8])*(uint64(in2[6])<<0) + tmp[15] = uint64(in[7])*(uint64(in2[8])<<0) + + uint64(in[8])*(uint64(in2[7])<<0) + tmp[16] = uint64(in[8]) * (uint64(in2[8]) << 0) + + p256ReduceDegree(out, tmp) +} + +func p256Assign(out, in *[p256Limbs]uint32) { + *out = *in +} + +// p256Invert calculates |out| = |in|^{-1} +// +// Based on Fermat's Little Theorem: +// a^p = a (mod p) +// a^{p-1} = 1 (mod p) +// a^{p-2} = a^{-1} (mod p) +func p256Invert(out, in *[p256Limbs]uint32) { + var ftmp, ftmp2 [p256Limbs]uint32 + + // each e_I will hold |in|^{2^I - 1} + var e2, e4, e8, e16, e32, e64 [p256Limbs]uint32 + + p256Square(&ftmp, in) // 2^1 + p256Mul(&ftmp, in, &ftmp) // 2^2 - 2^0 + p256Assign(&e2, &ftmp) + p256Square(&ftmp, &ftmp) // 2^3 - 2^1 + p256Square(&ftmp, &ftmp) // 2^4 - 2^2 + p256Mul(&ftmp, &ftmp, &e2) // 2^4 - 2^0 + p256Assign(&e4, &ftmp) + p256Square(&ftmp, &ftmp) // 2^5 - 2^1 + p256Square(&ftmp, &ftmp) // 2^6 - 2^2 + p256Square(&ftmp, &ftmp) // 2^7 - 2^3 + p256Square(&ftmp, &ftmp) // 2^8 - 2^4 + p256Mul(&ftmp, &ftmp, &e4) // 2^8 - 2^0 + p256Assign(&e8, &ftmp) + for i := 0; i < 8; i++ { + p256Square(&ftmp, &ftmp) + } // 2^16 - 2^8 + p256Mul(&ftmp, &ftmp, &e8) // 2^16 - 2^0 + p256Assign(&e16, &ftmp) + for i := 0; i < 16; i++ { + p256Square(&ftmp, &ftmp) + } // 2^32 - 2^16 + p256Mul(&ftmp, &ftmp, &e16) // 2^32 - 2^0 + p256Assign(&e32, &ftmp) + for i := 0; i < 32; i++ { + p256Square(&ftmp, &ftmp) + } // 2^64 - 2^32 + p256Assign(&e64, &ftmp) + p256Mul(&ftmp, &ftmp, in) // 2^64 - 2^32 + 2^0 + for i := 0; i < 192; i++ { + p256Square(&ftmp, &ftmp) + } // 2^256 - 2^224 + 2^192 + + p256Mul(&ftmp2, &e64, &e32) // 2^64 - 2^0 + for i := 0; i < 16; i++ { + p256Square(&ftmp2, &ftmp2) + } // 2^80 - 2^16 + p256Mul(&ftmp2, &ftmp2, &e16) // 2^80 - 2^0 + for i := 0; i < 8; i++ { + p256Square(&ftmp2, &ftmp2) + } // 2^88 - 2^8 + p256Mul(&ftmp2, &ftmp2, &e8) // 2^88 - 2^0 + for i := 0; i < 4; i++ { + p256Square(&ftmp2, &ftmp2) + } // 2^92 - 2^4 + p256Mul(&ftmp2, &ftmp2, &e4) // 2^92 - 2^0 + p256Square(&ftmp2, &ftmp2) // 2^93 - 2^1 + p256Square(&ftmp2, &ftmp2) // 2^94 - 2^2 + p256Mul(&ftmp2, &ftmp2, &e2) // 2^94 - 2^0 + p256Square(&ftmp2, &ftmp2) // 2^95 - 2^1 + p256Square(&ftmp2, &ftmp2) // 2^96 - 2^2 + p256Mul(&ftmp2, &ftmp2, in) // 2^96 - 3 + + p256Mul(out, &ftmp2, &ftmp) // 2^256 - 2^224 + 2^192 + 2^96 - 3 +} + +// p256Scalar3 sets out=3*out. +// +// On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +func p256Scalar3(out *[p256Limbs]uint32) { + var carry uint32 + + for i := 0; ; i++ { + out[i] *= 3 + out[i] += carry + carry = out[i] >> 29 + out[i] &= bottom29Bits + + i++ + if i == p256Limbs { + break + } + + out[i] *= 3 + out[i] += carry + carry = out[i] >> 28 + out[i] &= bottom28Bits + } + + p256ReduceCarry(out, carry) +} + +// p256Scalar4 sets out=4*out. +// +// On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +func p256Scalar4(out *[p256Limbs]uint32) { + var carry, nextCarry uint32 + + for i := 0; ; i++ { + nextCarry = out[i] >> 27 + out[i] <<= 2 + out[i] &= bottom29Bits + out[i] += carry + carry = nextCarry + (out[i] >> 29) + out[i] &= bottom29Bits + + i++ + if i == p256Limbs { + break + } + nextCarry = out[i] >> 26 + out[i] <<= 2 + out[i] &= bottom28Bits + out[i] += carry + carry = nextCarry + (out[i] >> 28) + out[i] &= bottom28Bits + } + + p256ReduceCarry(out, carry) +} + +// p256Scalar8 sets out=8*out. +// +// On entry: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29. +func p256Scalar8(out *[p256Limbs]uint32) { + var carry, nextCarry uint32 + + for i := 0; ; i++ { + nextCarry = out[i] >> 26 + out[i] <<= 3 + out[i] &= bottom29Bits + out[i] += carry + carry = nextCarry + (out[i] >> 29) + out[i] &= bottom29Bits + + i++ + if i == p256Limbs { + break + } + nextCarry = out[i] >> 25 + out[i] <<= 3 + out[i] &= bottom28Bits + out[i] += carry + carry = nextCarry + (out[i] >> 28) + out[i] &= bottom28Bits + } + + p256ReduceCarry(out, carry) +} + +// Group operations: +// +// Elements of the elliptic curve group are represented in Jacobian +// coordinates: (x, y, z). An affine point (x', y') is x'=x/z**2, y'=y/z**3 in +// Jacobian form. + +// p256PointDouble sets {xOut,yOut,zOut} = 2*{x,y,z}. +// +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l +func p256PointDouble(xOut, yOut, zOut, x, y, z *[p256Limbs]uint32) { + var delta, gamma, alpha, beta, tmp, tmp2 [p256Limbs]uint32 + + p256Square(&delta, z) + p256Square(&gamma, y) + p256Mul(&beta, x, &gamma) + + p256Sum(&tmp, x, &delta) + p256Diff(&tmp2, x, &delta) + p256Mul(&alpha, &tmp, &tmp2) + p256Scalar3(&alpha) + + p256Sum(&tmp, y, z) + p256Square(&tmp, &tmp) + p256Diff(&tmp, &tmp, &gamma) + p256Diff(zOut, &tmp, &delta) + + p256Scalar4(&beta) + p256Square(xOut, &alpha) + p256Diff(xOut, xOut, &beta) + p256Diff(xOut, xOut, &beta) + + p256Diff(&tmp, &beta, xOut) + p256Mul(&tmp, &alpha, &tmp) + p256Square(&tmp2, &gamma) + p256Scalar8(&tmp2) + p256Diff(yOut, &tmp, &tmp2) +} + +// p256PointAddMixed sets {xOut,yOut,zOut} = {x1,y1,z1} + {x2,y2,1}. +// (i.e. the second point is affine.) +// +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl +// +// Note that this function does not handle P+P, infinity+P nor P+infinity +// correctly. +func p256PointAddMixed(xOut, yOut, zOut, x1, y1, z1, x2, y2 *[p256Limbs]uint32) { + var z1z1, z1z1z1, s2, u2, h, i, j, r, rr, v, tmp [p256Limbs]uint32 + + p256Square(&z1z1, z1) + p256Sum(&tmp, z1, z1) + + p256Mul(&u2, x2, &z1z1) + p256Mul(&z1z1z1, z1, &z1z1) + p256Mul(&s2, y2, &z1z1z1) + p256Diff(&h, &u2, x1) + p256Sum(&i, &h, &h) + p256Square(&i, &i) + p256Mul(&j, &h, &i) + p256Diff(&r, &s2, y1) + p256Sum(&r, &r, &r) + p256Mul(&v, x1, &i) + + p256Mul(zOut, &tmp, &h) + p256Square(&rr, &r) + p256Diff(xOut, &rr, &j) + p256Diff(xOut, xOut, &v) + p256Diff(xOut, xOut, &v) + + p256Diff(&tmp, &v, xOut) + p256Mul(yOut, &tmp, &r) + p256Mul(&tmp, y1, &j) + p256Diff(yOut, yOut, &tmp) + p256Diff(yOut, yOut, &tmp) +} + +// p256PointAdd sets {xOut,yOut,zOut} = {x1,y1,z1} + {x2,y2,z2}. +// +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl +// +// Note that this function does not handle P+P, infinity+P nor P+infinity +// correctly. +func p256PointAdd(xOut, yOut, zOut, x1, y1, z1, x2, y2, z2 *[p256Limbs]uint32) { + var z1z1, z1z1z1, z2z2, z2z2z2, s1, s2, u1, u2, h, i, j, r, rr, v, tmp [p256Limbs]uint32 + + p256Square(&z1z1, z1) + p256Square(&z2z2, z2) + p256Mul(&u1, x1, &z2z2) + + p256Sum(&tmp, z1, z2) + p256Square(&tmp, &tmp) + p256Diff(&tmp, &tmp, &z1z1) + p256Diff(&tmp, &tmp, &z2z2) + + p256Mul(&z2z2z2, z2, &z2z2) + p256Mul(&s1, y1, &z2z2z2) + + p256Mul(&u2, x2, &z1z1) + p256Mul(&z1z1z1, z1, &z1z1) + p256Mul(&s2, y2, &z1z1z1) + p256Diff(&h, &u2, &u1) + p256Sum(&i, &h, &h) + p256Square(&i, &i) + p256Mul(&j, &h, &i) + p256Diff(&r, &s2, &s1) + p256Sum(&r, &r, &r) + p256Mul(&v, &u1, &i) + + p256Mul(zOut, &tmp, &h) + p256Square(&rr, &r) + p256Diff(xOut, &rr, &j) + p256Diff(xOut, xOut, &v) + p256Diff(xOut, xOut, &v) + + p256Diff(&tmp, &v, xOut) + p256Mul(yOut, &tmp, &r) + p256Mul(&tmp, &s1, &j) + p256Diff(yOut, yOut, &tmp) + p256Diff(yOut, yOut, &tmp) +} + +// p256CopyConditional sets out=in if mask = 0xffffffff in constant time. +// +// On entry: mask is either 0 or 0xffffffff. +func p256CopyConditional(out, in *[p256Limbs]uint32, mask uint32) { + for i := 0; i < p256Limbs; i++ { + tmp := mask & (in[i] ^ out[i]) + out[i] ^= tmp + } +} + +// p256SelectAffinePoint sets {out_x,out_y} to the index'th entry of table. +// On entry: index < 16, table[0] must be zero. +func p256SelectAffinePoint(xOut, yOut *[p256Limbs]uint32, table []uint32, index uint32) { + for i := range xOut { + xOut[i] = 0 + } + for i := range yOut { + yOut[i] = 0 + } + + for i := uint32(1); i < 16; i++ { + mask := i ^ index + mask |= mask >> 2 + mask |= mask >> 1 + mask &= 1 + mask-- + for j := range xOut { + xOut[j] |= table[0] & mask + table = table[1:] + } + for j := range yOut { + yOut[j] |= table[0] & mask + table = table[1:] + } + } +} + +// p256SelectJacobianPoint sets {out_x,out_y,out_z} to the index'th entry of +// table. +// On entry: index < 16, table[0] must be zero. +func p256SelectJacobianPoint(xOut, yOut, zOut *[p256Limbs]uint32, table *[16][3][p256Limbs]uint32, index uint32) { + for i := range xOut { + xOut[i] = 0 + } + for i := range yOut { + yOut[i] = 0 + } + for i := range zOut { + zOut[i] = 0 + } + + // The implicit value at index 0 is all zero. We don't need to perform that + // iteration of the loop because we already set out_* to zero. + for i := uint32(1); i < 16; i++ { + mask := i ^ index + mask |= mask >> 2 + mask |= mask >> 1 + mask &= 1 + mask-- + for j := range xOut { + xOut[j] |= table[i][0][j] & mask + } + for j := range yOut { + yOut[j] |= table[i][1][j] & mask + } + for j := range zOut { + zOut[j] |= table[i][2][j] & mask + } + } +} + +// p256GetBit returns the bit'th bit of scalar. +func p256GetBit(scalar *[32]uint8, bit uint) uint32 { + return uint32(((scalar[bit>>3]) >> (bit & 7)) & 1) +} + +// p256ScalarBaseMult sets {xOut,yOut,zOut} = scalar*G where scalar is a +// little-endian number. Note that the value of scalar must be less than the +// order of the group. +func p256ScalarBaseMult(xOut, yOut, zOut *[p256Limbs]uint32, scalar *[32]uint8) { + nIsInfinityMask := ^uint32(0) + var pIsNoninfiniteMask, mask, tableOffset uint32 + var px, py, tx, ty, tz [p256Limbs]uint32 + + for i := range xOut { + xOut[i] = 0 + } + for i := range yOut { + yOut[i] = 0 + } + for i := range zOut { + zOut[i] = 0 + } + + // The loop adds bits at positions 0, 64, 128 and 192, followed by + // positions 32,96,160 and 224 and does this 32 times. + for i := uint(0); i < 32; i++ { + if i != 0 { + p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut) + } + tableOffset = 0 + for j := uint(0); j <= 32; j += 32 { + bit0 := p256GetBit(scalar, 31-i+j) + bit1 := p256GetBit(scalar, 95-i+j) + bit2 := p256GetBit(scalar, 159-i+j) + bit3 := p256GetBit(scalar, 223-i+j) + index := bit0 | (bit1 << 1) | (bit2 << 2) | (bit3 << 3) + + p256SelectAffinePoint(&px, &py, p256Precomputed[tableOffset:], index) + tableOffset += 30 * p256Limbs + + // Since scalar is less than the order of the group, we know that + // {xOut,yOut,zOut} != {px,py,1}, unless both are zero, which we handle + // below. + p256PointAddMixed(&tx, &ty, &tz, xOut, yOut, zOut, &px, &py) + // The result of pointAddMixed is incorrect if {xOut,yOut,zOut} is zero + // (a.k.a. the point at infinity). We handle that situation by + // copying the point from the table. + p256CopyConditional(xOut, &px, nIsInfinityMask) + p256CopyConditional(yOut, &py, nIsInfinityMask) + p256CopyConditional(zOut, &p256One, nIsInfinityMask) + + // Equally, the result is also wrong if the point from the table is + // zero, which happens when the index is zero. We handle that by + // only copying from {tx,ty,tz} to {xOut,yOut,zOut} if index != 0. + pIsNoninfiniteMask = nonZeroToAllOnes(index) + mask = pIsNoninfiniteMask & ^nIsInfinityMask + p256CopyConditional(xOut, &tx, mask) + p256CopyConditional(yOut, &ty, mask) + p256CopyConditional(zOut, &tz, mask) + // If p was not zero, then n is now non-zero. + nIsInfinityMask &= ^pIsNoninfiniteMask + } + } +} + +// p256PointToAffine converts a Jacobian point to an affine point. If the input +// is the point at infinity then it returns (0, 0) in constant time. +func p256PointToAffine(xOut, yOut, x, y, z *[p256Limbs]uint32) { + var zInv, zInvSq [p256Limbs]uint32 + + p256Invert(&zInv, z) + p256Square(&zInvSq, &zInv) + p256Mul(xOut, x, &zInvSq) + p256Mul(&zInv, &zInv, &zInvSq) + p256Mul(yOut, y, &zInv) +} + +// p256ToAffine returns a pair of *big.Int containing the affine representation +// of {x,y,z}. +func p256ToAffine(x, y, z *[p256Limbs]uint32) (xOut, yOut *big.Int) { + var xx, yy [p256Limbs]uint32 + p256PointToAffine(&xx, &yy, x, y, z) + return p256ToBig(&xx), p256ToBig(&yy) +} + +// p256ScalarMult sets {xOut,yOut,zOut} = scalar*{x,y}. +func p256ScalarMult(xOut, yOut, zOut, x, y *[p256Limbs]uint32, scalar *[32]uint8) { + var px, py, pz, tx, ty, tz [p256Limbs]uint32 + var precomp [16][3][p256Limbs]uint32 + var nIsInfinityMask, index, pIsNoninfiniteMask, mask uint32 + + // We precompute 0,1,2,... times {x,y}. + precomp[1][0] = *x + precomp[1][1] = *y + precomp[1][2] = p256One + + for i := 2; i < 16; i += 2 { + p256PointDouble(&precomp[i][0], &precomp[i][1], &precomp[i][2], &precomp[i/2][0], &precomp[i/2][1], &precomp[i/2][2]) + p256PointAddMixed(&precomp[i+1][0], &precomp[i+1][1], &precomp[i+1][2], &precomp[i][0], &precomp[i][1], &precomp[i][2], x, y) + } + + for i := range xOut { + xOut[i] = 0 + } + for i := range yOut { + yOut[i] = 0 + } + for i := range zOut { + zOut[i] = 0 + } + nIsInfinityMask = ^uint32(0) + + // We add in a window of four bits each iteration and do this 64 times. + for i := 0; i < 64; i++ { + if i != 0 { + p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut) + p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut) + p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut) + p256PointDouble(xOut, yOut, zOut, xOut, yOut, zOut) + } + + index = uint32(scalar[31-i/2]) + if (i & 1) == 1 { + index &= 15 + } else { + index >>= 4 + } + + // See the comments in scalarBaseMult about handling infinities. + p256SelectJacobianPoint(&px, &py, &pz, &precomp, index) + p256PointAdd(&tx, &ty, &tz, xOut, yOut, zOut, &px, &py, &pz) + p256CopyConditional(xOut, &px, nIsInfinityMask) + p256CopyConditional(yOut, &py, nIsInfinityMask) + p256CopyConditional(zOut, &pz, nIsInfinityMask) + + pIsNoninfiniteMask = nonZeroToAllOnes(index) + mask = pIsNoninfiniteMask & ^nIsInfinityMask + p256CopyConditional(xOut, &tx, mask) + p256CopyConditional(yOut, &ty, mask) + p256CopyConditional(zOut, &tz, mask) + nIsInfinityMask &= ^pIsNoninfiniteMask + } +} + +// p256FromBig sets out = R*in. +func p256FromBig(out *[p256Limbs]uint32, in *big.Int) { + tmp := new(big.Int).Lsh(in, 257) + tmp.Mod(tmp, p256.P) + + for i := 0; i < p256Limbs; i++ { + if bits := tmp.Bits(); len(bits) > 0 { + out[i] = uint32(bits[0]) & bottom29Bits + } else { + out[i] = 0 + } + tmp.Rsh(tmp, 29) + + i++ + if i == p256Limbs { + break + } + + if bits := tmp.Bits(); len(bits) > 0 { + out[i] = uint32(bits[0]) & bottom28Bits + } else { + out[i] = 0 + } + tmp.Rsh(tmp, 28) + } +} + +// p256ToBig returns a *big.Int containing the value of in. +func p256ToBig(in *[p256Limbs]uint32) *big.Int { + result, tmp := new(big.Int), new(big.Int) + + result.SetInt64(int64(in[p256Limbs-1])) + for i := p256Limbs - 2; i >= 0; i-- { + if (i & 1) == 0 { + result.Lsh(result, 29) + } else { + result.Lsh(result, 28) + } + tmp.SetInt64(int64(in[i])) + result.Add(result, tmp) + } + + result.Mul(result, p256RInverse) + result.Mod(result, p256.P) + return result +} diff --git a/libgo/go/crypto/md5/gen.go b/libgo/go/crypto/md5/gen.go index 275b4ae..ccaa7c1 100644 --- a/libgo/go/crypto/md5/gen.go +++ b/libgo/go/crypto/md5/gen.go @@ -164,7 +164,7 @@ var program = ` // DO NOT EDIT. // Generate with: go run gen.go{{if .Full}} -full{{end}} | gofmt >md5block.go -// +build !amd64 +// +build !amd64,!386,!arm package md5 diff --git a/libgo/go/crypto/md5/md5.go b/libgo/go/crypto/md5/md5.go index 825e5c8..1a1f35f 100644 --- a/libgo/go/crypto/md5/md5.go +++ b/libgo/go/crypto/md5/md5.go @@ -88,7 +88,11 @@ func (d *digest) Write(p []byte) (nn int, err error) { func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := *d0 + hash := d.checkSum() + return append(in, hash[:]...) +} +func (d *digest) checkSum() [Size]byte { // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. len := d.len var tmp [64]byte @@ -118,5 +122,13 @@ func (d0 *digest) Sum(in []byte) []byte { digest[i*4+3] = byte(s >> 24) } - return append(in, digest[:]...) + return digest +} + +// Sum returns the MD5 checksum of the data. +func Sum(data []byte) [Size]byte { + var d digest + d.Reset() + d.Write(data) + return d.checkSum() } diff --git a/libgo/go/crypto/md5/md5_test.go b/libgo/go/crypto/md5/md5_test.go index 3ef4519..a8b7a1a 100644 --- a/libgo/go/crypto/md5/md5_test.go +++ b/libgo/go/crypto/md5/md5_test.go @@ -53,6 +53,10 @@ var golden = []md5Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] + s := fmt.Sprintf("%x", Sum([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum function: md5(%s) = %s want %s", g.in, s, g.out) + } c := New() buf := make([]byte, len(g.in)+4) for j := 0; j < 3+4; j++ { @@ -77,12 +81,28 @@ func TestGolden(t *testing.T) { } } -func ExampleNew() { - h := New() - io.WriteString(h, "The fog is getting thicker!") - io.WriteString(h, "And Leon's getting laaarger!") - fmt.Printf("%x", h.Sum(nil)) - // Output: e2c569be17396eca2a2e3c11578123ed +func TestLarge(t *testing.T) { + const N = 10000 + ok := "2bb571599a4180e1d542f76904adc3df" // md5sum of "0123456789" * 1000 + block := make([]byte, 10004) + c := New() + for offset := 0; offset < 4; offset++ { + for i := 0; i < N; i++ { + block[offset+i] = '0' + byte(i%10) + } + for blockSize := 10; blockSize <= N; blockSize *= 10 { + blocks := N / blockSize + b := block[offset : offset+blockSize] + c.Reset() + for i := 0; i < blocks; i++ { + c.Write(b) + } + s := fmt.Sprintf("%x", c.Sum(nil)) + if s != ok { + t.Fatalf("md5 TestLarge offset=%d, blockSize=%d = %s want %s", offset, blockSize, s, ok) + } + } + } } var bench = New() diff --git a/libgo/go/crypto/md5/md5block.go b/libgo/go/crypto/md5/md5block.go index a376fbe..3e739e3 100644 --- a/libgo/go/crypto/md5/md5block.go +++ b/libgo/go/crypto/md5/md5block.go @@ -1,7 +1,7 @@ // DO NOT EDIT. // Generate with: go run gen.go -full | gofmt >md5block.go -// +build !amd64,!386 +// +build !amd64,!386,!arm package md5 diff --git a/libgo/go/crypto/md5/md5block_decl.go b/libgo/go/crypto/md5/md5block_decl.go index 14190c6..c4d6aaa 100644 --- a/libgo/go/crypto/md5/md5block_decl.go +++ b/libgo/go/crypto/md5/md5block_decl.go @@ -2,8 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build amd64 386 +// +build amd64 386 arm package md5 +//go:noescape + func block(dig *digest, p []byte) diff --git a/libgo/go/crypto/rand/rand.go b/libgo/go/crypto/rand/rand.go index 5975903..4da3adb 100644 --- a/libgo/go/crypto/rand/rand.go +++ b/libgo/go/crypto/rand/rand.go @@ -14,5 +14,8 @@ import "io" // On Windows systems, Reader uses the CryptGenRandom API. var Reader io.Reader -// Read is a helper function that calls Reader.Read. -func Read(b []byte) (n int, err error) { return Reader.Read(b) } +// Read is a helper function that calls Reader.Read using io.ReadFull. +// On return, n == len(b) if and only if err == nil. +func Read(b []byte) (n int, err error) { + return io.ReadFull(Reader, b) +} diff --git a/libgo/go/crypto/rand/rand_unix.go b/libgo/go/crypto/rand/rand_unix.go index 18f4824..238ceee 100644 --- a/libgo/go/crypto/rand/rand_unix.go +++ b/libgo/go/crypto/rand/rand_unix.go @@ -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 freebsd linux netbsd openbsd plan9 +// +build darwin dragonfly freebsd linux netbsd openbsd plan9 // Unix cryptographically secure pseudorandom number // generator. diff --git a/libgo/go/crypto/rsa/pkcs1v15.go b/libgo/go/crypto/rsa/pkcs1v15.go index 28ca5d7..1a055a3 100644 --- a/libgo/go/crypto/rsa/pkcs1v15.go +++ b/libgo/go/crypto/rsa/pkcs1v15.go @@ -124,7 +124,11 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex) } - valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) + // The PS padding must be at least 8 bytes long, and it starts two + // bytes into em. + validPS := subtle.ConstantTimeLessOrEq(2+8, index) + + valid = firstByteIsZero & secondByteIsTwo & (^lookingForIndex & 1) & validPS msg = em[index+1:] return } diff --git a/libgo/go/crypto/rsa/pkcs1v15_test.go b/libgo/go/crypto/rsa/pkcs1v15_test.go index bf9219b..70bb228 100644 --- a/libgo/go/crypto/rsa/pkcs1v15_test.go +++ b/libgo/go/crypto/rsa/pkcs1v15_test.go @@ -197,6 +197,14 @@ func TestVerifyPKCS1v15(t *testing.T) { } } +func TestOverlongMessagePKCS1v15(t *testing.T) { + ciphertext := decodeBase64("fjOVdirUzFoLlukv80dBllMLjXythIf22feqPrNo0YoIjzyzyoMFiLjAc/Y4krkeZ11XFThIrEvw\nkRiZcCq5ng==") + _, err := DecryptPKCS1v15(nil, rsaPrivateKey, ciphertext) + if err == nil { + t.Error("RSA decrypted a message that was too long.") + } +} + // In order to generate new test vectors you'll need the PEM form of this key: // -----BEGIN RSA PRIVATE KEY----- // MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 diff --git a/libgo/go/crypto/rsa/pss.go b/libgo/go/crypto/rsa/pss.go new file mode 100644 index 0000000..f9abec3 --- /dev/null +++ b/libgo/go/crypto/rsa/pss.go @@ -0,0 +1,282 @@ +// 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 rsa + +// This file implementes the PSS signature scheme [1]. +// +// [1] http://www.rsa.com/rsalabs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf + +import ( + "bytes" + "crypto" + "errors" + "hash" + "io" + "math/big" +) + +func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) { + // See [1], section 9.1.1 + hLen := hash.Size() + sLen := len(salt) + emLen := (emBits + 7) / 8 + + // 1. If the length of M is greater than the input limitation for the + // hash function (2^61 - 1 octets for SHA-1), output "message too + // long" and stop. + // + // 2. Let mHash = Hash(M), an octet string of length hLen. + + if len(mHash) != hLen { + return nil, errors.New("crypto/rsa: input must be hashed message") + } + + // 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. + + if emLen < hLen+sLen+2 { + return nil, errors.New("crypto/rsa: encoding error") + } + + em := make([]byte, emLen) + db := em[:emLen-sLen-hLen-2+1+sLen] + h := em[emLen-sLen-hLen-2+1+sLen : emLen-1] + + // 4. Generate a random octet string salt of length sLen; if sLen = 0, + // then salt is the empty string. + // + // 5. Let + // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; + // + // M' is an octet string of length 8 + hLen + sLen with eight + // initial zero octets. + // + // 6. Let H = Hash(M'), an octet string of length hLen. + + var prefix [8]byte + + hash.Write(prefix[:]) + hash.Write(mHash) + hash.Write(salt) + + h = hash.Sum(h[:0]) + hash.Reset() + + // 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2 + // zero octets. The length of PS may be 0. + // + // 8. Let DB = PS || 0x01 || salt; DB is an octet string of length + // emLen - hLen - 1. + + db[emLen-sLen-hLen-2] = 0x01 + copy(db[emLen-sLen-hLen-1:], salt) + + // 9. Let dbMask = MGF(H, emLen - hLen - 1). + // + // 10. Let maskedDB = DB \xor dbMask. + + mgf1XOR(db, hash, h) + + // 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in + // maskedDB to zero. + + db[0] &= (0xFF >> uint(8*emLen-emBits)) + + // 12. Let EM = maskedDB || H || 0xbc. + em[emLen-1] = 0xBC + + // 13. Output EM. + return em, nil +} + +func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error { + // 1. If the length of M is greater than the input limitation for the + // hash function (2^61 - 1 octets for SHA-1), output "inconsistent" + // and stop. + // + // 2. Let mHash = Hash(M), an octet string of length hLen. + hLen := hash.Size() + if hLen != len(mHash) { + return ErrVerification + } + + // 3. If emLen < hLen + sLen + 2, output "inconsistent" and stop. + emLen := (emBits + 7) / 8 + if emLen < hLen+sLen+2 { + return ErrVerification + } + + // 4. If the rightmost octet of EM does not have hexadecimal value + // 0xbc, output "inconsistent" and stop. + if em[len(em)-1] != 0xBC { + return ErrVerification + } + + // 5. Let maskedDB be the leftmost emLen - hLen - 1 octets of EM, and + // let H be the next hLen octets. + db := em[:emLen-hLen-1] + h := em[emLen-hLen-1 : len(em)-1] + + // 6. If the leftmost 8 * emLen - emBits bits of the leftmost octet in + // maskedDB are not all equal to zero, output "inconsistent" and + // stop. + if em[0]&(0xFF<<uint(8-(8*emLen-emBits))) != 0 { + return ErrVerification + } + + // 7. Let dbMask = MGF(H, emLen - hLen - 1). + // + // 8. Let DB = maskedDB \xor dbMask. + mgf1XOR(db, hash, h) + + // 9. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in DB + // to zero. + db[0] &= (0xFF >> uint(8*emLen-emBits)) + + if sLen == PSSSaltLengthAuto { + FindSaltLength: + for sLen = emLen - (hLen + 2); sLen >= 0; sLen-- { + switch db[emLen-hLen-sLen-2] { + case 1: + break FindSaltLength + case 0: + continue + default: + return ErrVerification + } + } + if sLen < 0 { + return ErrVerification + } + } else { + // 10. If the emLen - hLen - sLen - 2 leftmost octets of DB are not zero + // or if the octet at position emLen - hLen - sLen - 1 (the leftmost + // position is "position 1") does not have hexadecimal value 0x01, + // output "inconsistent" and stop. + for _, e := range db[:emLen-hLen-sLen-2] { + if e != 0x00 { + return ErrVerification + } + } + if db[emLen-hLen-sLen-2] != 0x01 { + return ErrVerification + } + } + + // 11. Let salt be the last sLen octets of DB. + salt := db[len(db)-sLen:] + + // 12. Let + // M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ; + // M' is an octet string of length 8 + hLen + sLen with eight + // initial zero octets. + // + // 13. Let H' = Hash(M'), an octet string of length hLen. + var prefix [8]byte + hash.Write(prefix[:]) + hash.Write(mHash) + hash.Write(salt) + + h0 := hash.Sum(nil) + + // 14. If H = H', output "consistent." Otherwise, output "inconsistent." + if !bytes.Equal(h0, h) { + return ErrVerification + } + return nil +} + +// 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 +// 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() + em, err := emsaPSSEncode(hashed, nBits-1, salt, hash.New()) + if err != nil { + return + } + m := new(big.Int).SetBytes(em) + c, err := decrypt(rand, priv, m) + if err != nil { + return + } + s = make([]byte, (nBits+7)/8) + copyWithLeftPad(s, c.Bytes()) + return +} + +const ( + // PSSSaltLengthAuto causes the salt in a PSS signature to be as large + // as possible when signing, and to be auto-detected when verifying. + PSSSaltLengthAuto = 0 + // PSSSaltLengthEqualsHash causes the salt length to equal the length + // of the hash used in the signature. + PSSSaltLengthEqualsHash = -1 +) + +// PSSOptions contains options for creating and verifying PSS signatures. +type PSSOptions struct { + // SaltLength controls the length of the salt used in the PSS + // signature. It can either be a number of bytes, or one of the special + // PSSSaltLength constants. + SaltLength int +} + +func (opts *PSSOptions) saltLength() int { + if opts == nil { + return PSSSaltLengthAuto + } + return opts.SaltLength +} + +// 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 +// defaults are used. +func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) (s []byte, err error) { + saltLength := opts.saltLength() + switch saltLength { + case PSSSaltLengthAuto: + saltLength = (priv.N.BitLen()+7)/8 - 2 - hash.Size() + case PSSSaltLengthEqualsHash: + saltLength = hash.Size() + } + + salt := make([]byte, saltLength) + if _, err = io.ReadFull(rand, salt); err != nil { + return + } + return signPSSWithSalt(rand, priv, hash, hashed, salt) +} + +// VerifyPSS verifies a PSS signature. +// 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. The opts argument may be nil, in which case sensible +// defaults are used. +func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts *PSSOptions) error { + return verifyPSS(pub, hash, hashed, sig, opts.saltLength()) +} + +// verifyPSS verifies a PSS signature with the given salt length. +func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error { + nBits := pub.N.BitLen() + if len(sig) != (nBits+7)/8 { + return ErrVerification + } + s := new(big.Int).SetBytes(sig) + m := encrypt(new(big.Int), pub, s) + emBits := nBits - 1 + emLen := (emBits + 7) / 8 + if emLen < len(m.Bytes()) { + return ErrVerification + } + em := make([]byte, emLen) + copyWithLeftPad(em, m.Bytes()) + if saltLen == PSSSaltLengthEqualsHash { + saltLen = hash.Size() + } + return emsaPSSVerify(hashed, em, emBits, saltLen, hash.New()) +} diff --git a/libgo/go/crypto/rsa/pss_test.go b/libgo/go/crypto/rsa/pss_test.go new file mode 100644 index 0000000..32e6fc3 --- /dev/null +++ b/libgo/go/crypto/rsa/pss_test.go @@ -0,0 +1,249 @@ +// 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 rsa + +import ( + "bufio" + "bytes" + "compress/bzip2" + "crypto" + _ "crypto/md5" + "crypto/rand" + "crypto/sha1" + _ "crypto/sha256" + "encoding/hex" + "math/big" + "os" + "strconv" + "strings" + "testing" +) + +func TestEMSAPSS(t *testing.T) { + // Test vector in file pss-int.txt from: ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip + msg := []byte{ + 0x85, 0x9e, 0xef, 0x2f, 0xd7, 0x8a, 0xca, 0x00, 0x30, 0x8b, + 0xdc, 0x47, 0x11, 0x93, 0xbf, 0x55, 0xbf, 0x9d, 0x78, 0xdb, + 0x8f, 0x8a, 0x67, 0x2b, 0x48, 0x46, 0x34, 0xf3, 0xc9, 0xc2, + 0x6e, 0x64, 0x78, 0xae, 0x10, 0x26, 0x0f, 0xe0, 0xdd, 0x8c, + 0x08, 0x2e, 0x53, 0xa5, 0x29, 0x3a, 0xf2, 0x17, 0x3c, 0xd5, + 0x0c, 0x6d, 0x5d, 0x35, 0x4f, 0xeb, 0xf7, 0x8b, 0x26, 0x02, + 0x1c, 0x25, 0xc0, 0x27, 0x12, 0xe7, 0x8c, 0xd4, 0x69, 0x4c, + 0x9f, 0x46, 0x97, 0x77, 0xe4, 0x51, 0xe7, 0xf8, 0xe9, 0xe0, + 0x4c, 0xd3, 0x73, 0x9c, 0x6b, 0xbf, 0xed, 0xae, 0x48, 0x7f, + 0xb5, 0x56, 0x44, 0xe9, 0xca, 0x74, 0xff, 0x77, 0xa5, 0x3c, + 0xb7, 0x29, 0x80, 0x2f, 0x6e, 0xd4, 0xa5, 0xff, 0xa8, 0xba, + 0x15, 0x98, 0x90, 0xfc, + } + salt := []byte{ + 0xe3, 0xb5, 0xd5, 0xd0, 0x02, 0xc1, 0xbc, 0xe5, 0x0c, 0x2b, + 0x65, 0xef, 0x88, 0xa1, 0x88, 0xd8, 0x3b, 0xce, 0x7e, 0x61, + } + expected := []byte{ + 0x66, 0xe4, 0x67, 0x2e, 0x83, 0x6a, 0xd1, 0x21, 0xba, 0x24, + 0x4b, 0xed, 0x65, 0x76, 0xb8, 0x67, 0xd9, 0xa4, 0x47, 0xc2, + 0x8a, 0x6e, 0x66, 0xa5, 0xb8, 0x7d, 0xee, 0x7f, 0xbc, 0x7e, + 0x65, 0xaf, 0x50, 0x57, 0xf8, 0x6f, 0xae, 0x89, 0x84, 0xd9, + 0xba, 0x7f, 0x96, 0x9a, 0xd6, 0xfe, 0x02, 0xa4, 0xd7, 0x5f, + 0x74, 0x45, 0xfe, 0xfd, 0xd8, 0x5b, 0x6d, 0x3a, 0x47, 0x7c, + 0x28, 0xd2, 0x4b, 0xa1, 0xe3, 0x75, 0x6f, 0x79, 0x2d, 0xd1, + 0xdc, 0xe8, 0xca, 0x94, 0x44, 0x0e, 0xcb, 0x52, 0x79, 0xec, + 0xd3, 0x18, 0x3a, 0x31, 0x1f, 0xc8, 0x96, 0xda, 0x1c, 0xb3, + 0x93, 0x11, 0xaf, 0x37, 0xea, 0x4a, 0x75, 0xe2, 0x4b, 0xdb, + 0xfd, 0x5c, 0x1d, 0xa0, 0xde, 0x7c, 0xec, 0xdf, 0x1a, 0x89, + 0x6f, 0x9d, 0x8b, 0xc8, 0x16, 0xd9, 0x7c, 0xd7, 0xa2, 0xc4, + 0x3b, 0xad, 0x54, 0x6f, 0xbe, 0x8c, 0xfe, 0xbc, + } + + hash := sha1.New() + hash.Write(msg) + hashed := hash.Sum(nil) + + encoded, err := emsaPSSEncode(hashed, 1023, salt, sha1.New()) + if err != nil { + t.Errorf("Error from emsaPSSEncode: %s\n", err) + } + if !bytes.Equal(encoded, expected) { + t.Errorf("Bad encoding. got %x, want %x", encoded, expected) + } + + if err = emsaPSSVerify(hashed, encoded, 1023, len(salt), sha1.New()); err != nil { + t.Errorf("Bad verification: %s", err) + } +} + +// TestPSSGolden tests all the test vectors in pss-vect.txt from +// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip +func TestPSSGolden(t *testing.T) { + inFile, err := os.Open("testdata/pss-vect.txt.bz2") + if err != nil { + t.Fatalf("Failed to open input file: %s", err) + } + defer inFile.Close() + + // The pss-vect.txt file contains RSA keys and then a series of + // signatures. A goroutine is used to preprocess the input by merging + // lines, removing spaces in hex values and identifying the start of + // new keys and signature blocks. + const newKeyMarker = "START NEW KEY" + const newSignatureMarker = "START NEW SIGNATURE" + + values := make(chan string) + + go func() { + defer close(values) + scanner := bufio.NewScanner(bzip2.NewReader(inFile)) + var partialValue string + lastWasValue := true + + for scanner.Scan() { + line := scanner.Text() + switch { + case len(line) == 0: + if len(partialValue) > 0 { + values <- strings.Replace(partialValue, " ", "", -1) + partialValue = "" + lastWasValue = true + } + continue + case strings.HasPrefix(line, "# ======") && lastWasValue: + values <- newKeyMarker + lastWasValue = false + case strings.HasPrefix(line, "# ------") && lastWasValue: + values <- newSignatureMarker + lastWasValue = false + case strings.HasPrefix(line, "#"): + continue + default: + partialValue += line + } + } + if err := scanner.Err(); err != nil { + panic(err) + } + }() + + var key *PublicKey + var hashed []byte + hash := crypto.SHA1 + h := hash.New() + opts := &PSSOptions{ + SaltLength: PSSSaltLengthEqualsHash, + } + + for marker := range values { + switch marker { + case newKeyMarker: + key = new(PublicKey) + nHex, ok := <-values + if !ok { + continue + } + key.N = bigFromHex(nHex) + key.E = intFromHex(<-values) + // We don't care for d, p, q, dP, dQ or qInv. + for i := 0; i < 6; i++ { + <-values + } + case newSignatureMarker: + msg := fromHex(<-values) + <-values // skip salt + sig := fromHex(<-values) + + h.Reset() + h.Write(msg) + hashed = h.Sum(hashed[:0]) + + if err := VerifyPSS(key, hash, hashed, sig, opts); err != nil { + t.Error(err) + } + default: + t.Fatalf("unknown marker: " + marker) + } + } +} + +// TestPSSOpenSSL ensures that we can verify a PSS signature from OpenSSL with +// the default options. OpenSSL sets the salt length to be maximal. +func TestPSSOpenSSL(t *testing.T) { + hash := crypto.SHA256 + h := hash.New() + h.Write([]byte("testing")) + hashed := h.Sum(nil) + + // Generated with `echo -n testing | openssl dgst -sign key.pem -sigopt rsa_padding_mode:pss -sha256 > sig` + sig := []byte{ + 0x95, 0x59, 0x6f, 0xd3, 0x10, 0xa2, 0xe7, 0xa2, 0x92, 0x9d, + 0x4a, 0x07, 0x2e, 0x2b, 0x27, 0xcc, 0x06, 0xc2, 0x87, 0x2c, + 0x52, 0xf0, 0x4a, 0xcc, 0x05, 0x94, 0xf2, 0xc3, 0x2e, 0x20, + 0xd7, 0x3e, 0x66, 0x62, 0xb5, 0x95, 0x2b, 0xa3, 0x93, 0x9a, + 0x66, 0x64, 0x25, 0xe0, 0x74, 0x66, 0x8c, 0x3e, 0x92, 0xeb, + 0xc6, 0xe6, 0xc0, 0x44, 0xf3, 0xb4, 0xb4, 0x2e, 0x8c, 0x66, + 0x0a, 0x37, 0x9c, 0x69, + } + + if err := VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, nil); err != nil { + t.Error(err) + } +} + +func TestPSSSigning(t *testing.T) { + var saltLengthCombinations = []struct { + signSaltLength, verifySaltLength int + good bool + }{ + {PSSSaltLengthAuto, PSSSaltLengthAuto, true}, + {PSSSaltLengthEqualsHash, PSSSaltLengthAuto, true}, + {PSSSaltLengthEqualsHash, PSSSaltLengthEqualsHash, true}, + {PSSSaltLengthEqualsHash, 8, false}, + {PSSSaltLengthAuto, PSSSaltLengthEqualsHash, false}, + {8, 8, true}, + } + + hash := crypto.MD5 + h := hash.New() + h.Write([]byte("testing")) + hashed := h.Sum(nil) + var opts PSSOptions + + for i, test := range saltLengthCombinations { + opts.SaltLength = test.signSaltLength + sig, err := SignPSS(rand.Reader, rsaPrivateKey, hash, hashed, &opts) + if err != nil { + t.Errorf("#%d: error while signing: %s", i, err) + continue + } + + opts.SaltLength = test.verifySaltLength + err = VerifyPSS(&rsaPrivateKey.PublicKey, hash, hashed, sig, &opts) + if (err == nil) != test.good { + t.Errorf("#%d: bad result, wanted: %t, got: %s", i, test.good, err) + } + } +} + +func bigFromHex(hex string) *big.Int { + n, ok := new(big.Int).SetString(hex, 16) + if !ok { + panic("bad hex: " + hex) + } + return n +} + +func intFromHex(hex string) int { + i, err := strconv.ParseInt(hex, 16, 32) + if err != nil { + panic(err) + } + return int(i) +} + +func fromHex(hexStr string) []byte { + s, err := hex.DecodeString(hexStr) + if err != nil { + panic(err) + } + return s +} diff --git a/libgo/go/crypto/rsa/rsa.go b/libgo/go/crypto/rsa/rsa.go index f56fb37..c7353ea 100644 --- a/libgo/go/crypto/rsa/rsa.go +++ b/libgo/go/crypto/rsa/rsa.go @@ -5,8 +5,6 @@ // Package rsa implements RSA encryption as specified in PKCS#1. package rsa -// TODO(agl): Add support for PSS padding. - import ( "crypto/rand" "crypto/subtle" diff --git a/libgo/go/crypto/rsa/rsa_test.go b/libgo/go/crypto/rsa/rsa_test.go index ffd96e62..cf193c6 100644 --- a/libgo/go/crypto/rsa/rsa_test.go +++ b/libgo/go/crypto/rsa/rsa_test.go @@ -120,8 +120,10 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) { } func fromBase10(base10 string) *big.Int { - i := new(big.Int) - i.SetString(base10, 10) + i, ok := new(big.Int).SetString(base10, 10) + if !ok { + panic("bad number: " + base10) + } return i } diff --git a/libgo/go/crypto/rsa/testdata/pss-vect.txt.bz2 b/libgo/go/crypto/rsa/testdata/pss-vect.txt.bz2 Binary files differnew file mode 100644 index 0000000..ad3da1a --- /dev/null +++ b/libgo/go/crypto/rsa/testdata/pss-vect.txt.bz2 diff --git a/libgo/go/crypto/sha1/sha1.go b/libgo/go/crypto/sha1/sha1.go index 7cfde47..8eb3f7a 100644 --- a/libgo/go/crypto/sha1/sha1.go +++ b/libgo/go/crypto/sha1/sha1.go @@ -90,9 +90,13 @@ func (d *digest) Write(p []byte) (nn int, err error) { func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := *d0 + hash := d.checkSum() + return append(in, hash[:]...) +} - // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. +func (d *digest) checkSum() [Size]byte { len := d.len + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. var tmp [64]byte tmp[0] = 0x80 if len%64 < 56 { @@ -120,5 +124,13 @@ func (d0 *digest) Sum(in []byte) []byte { digest[i*4+3] = byte(s) } - return append(in, digest[:]...) + return digest +} + +// Sum returns the SHA1 checksum of the data. +func Sum(data []byte) [Size]byte { + var d digest + d.Reset() + d.Write(data) + return d.checkSum() } diff --git a/libgo/go/crypto/sha1/sha1_test.go b/libgo/go/crypto/sha1/sha1_test.go index 57cd431..c3868d7 100644 --- a/libgo/go/crypto/sha1/sha1_test.go +++ b/libgo/go/crypto/sha1/sha1_test.go @@ -54,6 +54,10 @@ var golden = []sha1Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] + s := fmt.Sprintf("%x", Sum([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum function: sha1(%s) = %s want %s", g.in, s, g.out) + } c := New() for j := 0; j < 3; j++ { if j < 2 { @@ -72,13 +76,6 @@ func TestGolden(t *testing.T) { } } -func ExampleNew() { - h := New() - io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.") - fmt.Printf("% x", h.Sum(nil)) - // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd -} - var bench = New() var buf = make([]byte, 8192) diff --git a/libgo/go/crypto/sha1/sha1block_decl.go b/libgo/go/crypto/sha1/sha1block_decl.go index 3512a58..4cb157f 100644 --- a/libgo/go/crypto/sha1/sha1block_decl.go +++ b/libgo/go/crypto/sha1/sha1block_decl.go @@ -6,4 +6,6 @@ package sha1 +//go:noescape + func block(dig *digest, p []byte) diff --git a/libgo/go/crypto/sha256/sha256.go b/libgo/go/crypto/sha256/sha256.go index dc0e18f..d69ed24 100644 --- a/libgo/go/crypto/sha256/sha256.go +++ b/libgo/go/crypto/sha256/sha256.go @@ -134,9 +134,16 @@ func (d *digest) Write(p []byte) (nn int, err error) { func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := *d0 + hash := d.checkSum() + if d.is224 { + return append(in, hash[:Size224]...) + } + return append(in, hash[:]...) +} - // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. +func (d *digest) checkSum() [Size]byte { len := d.len + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. var tmp [64]byte tmp[0] = 0x80 if len%64 < 56 { @@ -157,10 +164,8 @@ func (d0 *digest) Sum(in []byte) []byte { } h := d.h[:] - size := Size if d.is224 { h = d.h[:7] - size = Size224 } var digest [Size]byte @@ -171,5 +176,24 @@ func (d0 *digest) Sum(in []byte) []byte { digest[i*4+3] = byte(s) } - return append(in, digest[:size]...) + return digest +} + +// Sum256 returns the SHA256 checksum of the data. +func Sum256(data []byte) [Size]byte { + var d digest + d.Reset() + d.Write(data) + return d.checkSum() +} + +// Sum224 returns the SHA224 checksum of the data. +func Sum224(data []byte) (sum224 [Size224]byte) { + var d digest + d.is224 = true + d.Reset() + d.Write(data) + sum := d.checkSum() + copy(sum224[:], sum[:Size224]) + return } diff --git a/libgo/go/crypto/sha256/sha256_test.go b/libgo/go/crypto/sha256/sha256_test.go index 29bf1619..bb1ec3b 100644 --- a/libgo/go/crypto/sha256/sha256_test.go +++ b/libgo/go/crypto/sha256/sha256_test.go @@ -88,6 +88,10 @@ var golden224 = []sha256Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] + s := fmt.Sprintf("%x", Sum256([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum256 function: sha256(%s) = %s want %s", g.in, s, g.out) + } c := New() for j := 0; j < 3; j++ { if j < 2 { @@ -106,6 +110,10 @@ func TestGolden(t *testing.T) { } for i := 0; i < len(golden224); i++ { g := golden224[i] + s := fmt.Sprintf("%x", Sum224([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum224 function: sha224(%s) = %s want %s", g.in, s, g.out) + } c := New224() for j := 0; j < 3; j++ { if j < 2 { diff --git a/libgo/go/crypto/sha512/sha512.go b/libgo/go/crypto/sha512/sha512.go index 4aec529..d2ada51 100644 --- a/libgo/go/crypto/sha512/sha512.go +++ b/libgo/go/crypto/sha512/sha512.go @@ -135,7 +135,14 @@ func (d0 *digest) Sum(in []byte) []byte { // Make a copy of d0 so that caller can keep writing and summing. d := new(digest) *d = *d0 + hash := d.checkSum() + if d.is384 { + return append(in, hash[:Size384]...) + } + return append(in, hash[:]...) +} +func (d *digest) checkSum() [Size]byte { // Padding. Add a 1 bit and 0 bits until 112 bytes mod 128. len := d.len var tmp [128]byte @@ -158,10 +165,8 @@ func (d0 *digest) Sum(in []byte) []byte { } h := d.h[:] - size := Size if d.is384 { h = d.h[:6] - size = Size384 } var digest [Size]byte @@ -176,5 +181,24 @@ func (d0 *digest) Sum(in []byte) []byte { digest[i*8+7] = byte(s) } - return append(in, digest[:size]...) + return digest +} + +// Sum512 returns the SHA512 checksum of the data. +func Sum512(data []byte) [Size]byte { + var d digest + d.Reset() + d.Write(data) + return d.checkSum() +} + +// Sum384 returns the SHA384 checksum of the data. +func Sum384(data []byte) (sum384 [Size384]byte) { + var d digest + d.is384 = true + d.Reset() + d.Write(data) + sum := d.checkSum() + copy(sum384[:], sum[:Size384]) + return } diff --git a/libgo/go/crypto/sha512/sha512_test.go b/libgo/go/crypto/sha512/sha512_test.go index 6eafb1b..167c20a 100644 --- a/libgo/go/crypto/sha512/sha512_test.go +++ b/libgo/go/crypto/sha512/sha512_test.go @@ -88,6 +88,10 @@ var golden384 = []sha512Test{ func TestGolden(t *testing.T) { for i := 0; i < len(golden); i++ { g := golden[i] + s := fmt.Sprintf("%x", Sum512([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum512 function: sha512(%s) = %s want %s", g.in, s, g.out) + } c := New() for j := 0; j < 3; j++ { if j < 2 { @@ -106,6 +110,10 @@ func TestGolden(t *testing.T) { } for i := 0; i < len(golden384); i++ { g := golden384[i] + s := fmt.Sprintf("%x", Sum384([]byte(g.in))) + if s != g.out { + t.Fatalf("Sum384 function: sha384(%s) = %s want %s", g.in, s, g.out) + } c := New384() for j := 0; j < 3; j++ { if j < 2 { diff --git a/libgo/go/crypto/subtle/constant_time.go b/libgo/go/crypto/subtle/constant_time.go index 57dbe9d..dfb6584 100644 --- a/libgo/go/crypto/subtle/constant_time.go +++ b/libgo/go/crypto/subtle/constant_time.go @@ -55,3 +55,11 @@ func ConstantTimeCopy(v int, x, y []byte) { } return } + +// ConstantTimeLessOrEq returns 1 if x <= y and 0 otherwise. +// Its behavior is undefined if x or y are negative or > 2**31 - 1. +func ConstantTimeLessOrEq(x, y int) int { + x32 := int32(x) + y32 := int32(y) + return int(((x32 - y32 - 1) >> 31) & 1) +} diff --git a/libgo/go/crypto/subtle/constant_time_test.go b/libgo/go/crypto/subtle/constant_time_test.go index adab8e2..d8e321e 100644 --- a/libgo/go/crypto/subtle/constant_time_test.go +++ b/libgo/go/crypto/subtle/constant_time_test.go @@ -103,3 +103,23 @@ func TestConstantTimeCopy(t *testing.T) { t.Error(err) } } + +var lessOrEqTests = []struct { + x, y, result int +}{ + {0, 0, 1}, + {1, 0, 0}, + {0, 1, 1}, + {10, 20, 1}, + {20, 10, 0}, + {10, 10, 1}, +} + +func TestConstantTimeLessOrEq(t *testing.T) { + for i, test := range lessOrEqTests { + result := ConstantTimeLessOrEq(test.x, test.y) + if result != test.result { + t.Errorf("#%d: %d <= %d gave %d, expected %d", i, test.x, test.y, result, test.result) + } + } +} diff --git a/libgo/go/crypto/tls/cipher_suites.go b/libgo/go/crypto/tls/cipher_suites.go index a647e19..39a5145 100644 --- a/libgo/go/crypto/tls/cipher_suites.go +++ b/libgo/go/crypto/tls/cipher_suites.go @@ -34,6 +34,22 @@ type keyAgreement interface { generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) } +const ( + // suiteECDH indicates that the cipher suite involves elliptic curve + // Diffie-Hellman. This means that it should only be selected when the + // client indicates that it supports ECC with a curve and point format + // that we're happy with. + suiteECDHE = 1 << iota + // suiteECDSA indicates that the cipher suite involves an ECDSA + // signature and therefore may only be selected when the server's + // certificate is ECDSA. If this is not set then the cipher suite is + // RSA based. + suiteECDSA + // suiteTLS12 indicates that the cipher suite should only be advertised + // and accepted when using TLS 1.2. + suiteTLS12 +) + // A cipherSuite is a specific combination of key agreement, cipher and MAC // function. All cipher suites currently assume RSA key agreement. type cipherSuite struct { @@ -42,24 +58,30 @@ type cipherSuite struct { keyLen int macLen int ivLen int - ka func() keyAgreement - // If elliptic is set, a server will only consider this ciphersuite if - // the ClientHello indicated that the client supports an elliptic curve - // and point format that we can handle. - elliptic bool - cipher func(key, iv []byte, isRead bool) interface{} - mac func(version uint16, macKey []byte) macFunction + ka func(version uint16) keyAgreement + // flags is a bitmask of the suite* values, above. + flags int + cipher func(key, iv []byte, isRead bool) interface{} + mac func(version uint16, macKey []byte) macFunction + aead func(key, fixedNonce []byte) cipher.AEAD } var cipherSuites = []*cipherSuite{ - {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, false, cipherRC4, macSHA1}, - {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, false, cipher3DES, macSHA1}, - {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, false, cipherAES, macSHA1}, - {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, false, cipherAES, macSHA1}, - {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1}, - {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1}, - {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, - {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1}, + // Ciphersuite order is chosen so that ECDHE comes before plain RSA + // and RC4 comes before AES (because of the Lucky13 attack). + {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM}, + {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherRC4, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil}, + {TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil}, + {TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil}, + {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil}, + {TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil}, } func cipherRC4(key, iv []byte, isRead bool) interface{} { @@ -85,7 +107,7 @@ func cipherAES(key, iv []byte, isRead bool) interface{} { // macSHA1 returns a macFunction for the given protocol version. func macSHA1(version uint16, key []byte) macFunction { - if version == versionSSL30 { + if version == VersionSSL30 { mac := ssl30MAC{ h: sha1.New(), key: make([]byte, len(key)), @@ -98,7 +120,47 @@ func macSHA1(version uint16, key []byte) macFunction { type macFunction interface { Size() int - MAC(digestBuf, seq, data []byte) []byte + MAC(digestBuf, seq, header, data []byte) []byte +} + +// fixedNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to +// each call. +type fixedNonceAEAD struct { + // sealNonce and openNonce are buffers where the larger nonce will be + // constructed. Since a seal and open operation may be running + // concurrently, there is a separate buffer for each. + sealNonce, openNonce []byte + aead cipher.AEAD +} + +func (f *fixedNonceAEAD) NonceSize() int { return 8 } +func (f *fixedNonceAEAD) Overhead() int { return f.aead.Overhead() } + +func (f *fixedNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { + copy(f.sealNonce[len(f.sealNonce)-8:], nonce) + return f.aead.Seal(out, f.sealNonce, plaintext, additionalData) +} + +func (f *fixedNonceAEAD) Open(out, nonce, plaintext, additionalData []byte) ([]byte, error) { + copy(f.openNonce[len(f.openNonce)-8:], nonce) + return f.aead.Open(out, f.openNonce, plaintext, additionalData) +} + +func aeadAESGCM(key, fixedNonce []byte) cipher.AEAD { + aes, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + aead, err := cipher.NewGCM(aes) + if err != nil { + panic(err) + } + + nonce1, nonce2 := make([]byte, 12), make([]byte, 12) + copy(nonce1, fixedNonce) + copy(nonce2, fixedNonce) + + return &fixedNonceAEAD{nonce1, nonce2, aead} } // ssl30MAC implements the SSLv3 MAC function, as defined in @@ -116,7 +178,7 @@ var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0 var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c} -func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte { +func (s ssl30MAC) MAC(digestBuf, seq, header, data []byte) []byte { padLength := 48 if s.h.Size() == 20 { padLength = 40 @@ -126,9 +188,9 @@ func (s ssl30MAC) MAC(digestBuf, seq, record []byte) []byte { s.h.Write(s.key) s.h.Write(ssl30Pad1[:padLength]) s.h.Write(seq) - s.h.Write(record[:1]) - s.h.Write(record[3:5]) - s.h.Write(record[recordHeaderLen:]) + s.h.Write(header[:1]) + s.h.Write(header[3:5]) + s.h.Write(data) digestBuf = s.h.Sum(digestBuf[:0]) s.h.Reset() @@ -147,19 +209,30 @@ func (s tls10MAC) Size() int { return s.h.Size() } -func (s tls10MAC) MAC(digestBuf, seq, record []byte) []byte { +func (s tls10MAC) MAC(digestBuf, seq, header, data []byte) []byte { s.h.Reset() s.h.Write(seq) - s.h.Write(record) + s.h.Write(header) + s.h.Write(data) return s.h.Sum(digestBuf[:0]) } -func rsaKA() keyAgreement { +func rsaKA(version uint16) keyAgreement { return rsaKeyAgreement{} } -func ecdheRSAKA() keyAgreement { - return new(ecdheRSAKeyAgreement) +func ecdheECDSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + sigType: signatureECDSA, + version: version, + } +} + +func ecdheRSAKA(version uint16) keyAgreement { + return &ecdheKeyAgreement{ + sigType: signatureRSA, + version: version, + } } // mutualCipherSuite returns a cipherSuite given a list of supported @@ -181,12 +254,17 @@ func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { // A list of the possible cipher suite ids. Taken from // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml const ( - TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 - TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a - TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f - TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 - TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 - TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 + TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005 + TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a + TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f + TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035 + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007 + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009 + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a + TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011 + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012 + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013 + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014 + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b ) diff --git a/libgo/go/crypto/tls/common.go b/libgo/go/crypto/tls/common.go index f86c90d..b7229d2 100644 --- a/libgo/go/crypto/tls/common.go +++ b/libgo/go/crypto/tls/common.go @@ -9,22 +9,27 @@ import ( "crypto/rand" "crypto/x509" "io" + "math/big" "strings" "sync" "time" ) const ( + VersionSSL30 = 0x0300 + VersionTLS10 = 0x0301 + VersionTLS11 = 0x0302 + VersionTLS12 = 0x0303 +) + +const ( maxPlaintext = 16384 // maximum plaintext payload length maxCiphertext = 16384 + 2048 // maximum ciphertext payload length recordHeaderLen = 5 // record header length maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB) - versionSSL30 = 0x0300 - versionTLS10 = 0x0301 - - minVersion = versionSSL30 - maxVersion = versionTLS10 + minVersion = VersionSSL30 + maxVersion = VersionTLS12 ) // TLS record types. @@ -60,12 +65,13 @@ const ( // TLS extension numbers var ( - extensionServerName uint16 = 0 - extensionStatusRequest uint16 = 5 - extensionSupportedCurves uint16 = 10 - extensionSupportedPoints uint16 = 11 - extensionSessionTicket uint16 = 35 - extensionNextProtoNeg uint16 = 13172 // not IANA assigned + extensionServerName uint16 = 0 + extensionStatusRequest uint16 = 5 + extensionSupportedCurves uint16 = 10 + extensionSupportedPoints uint16 = 11 + extensionSignatureAlgorithms uint16 = 13 + extensionSessionTicket uint16 = 35 + extensionNextProtoNeg uint16 = 13172 // not IANA assigned ) // TLS Elliptic Curves @@ -93,25 +99,60 @@ const ( certTypeDSSSign = 2 // A certificate containing a DSA key certTypeRSAFixedDH = 3 // A certificate containing a static DH key certTypeDSSFixedDH = 4 // A certificate containing a static DH key + + // See RFC4492 sections 3 and 5.5. + certTypeECDSASign = 64 // A certificate containing an ECDSA-capable public key, signed with ECDSA. + certTypeRSAFixedECDH = 65 // A certificate containing an ECDH-capable public key, signed with RSA. + certTypeECDSAFixedECDH = 66 // A certificate containing an ECDH-capable public key, signed with ECDSA. + // Rest of these are reserved by the TLS spec ) +// Hash functions for TLS 1.2 (See RFC 5246, section A.4.1) +const ( + hashSHA1 uint8 = 2 + hashSHA256 uint8 = 4 +) + +// Signature algorithms for TLS 1.2 (See RFC 5246, section A.4.1) +const ( + signatureRSA uint8 = 1 + signatureECDSA uint8 = 3 +) + +// signatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See +// RFC 5246, section A.4.1. +type signatureAndHash struct { + hash, signature uint8 +} + +// supportedSKXSignatureAlgorithms contains the signature and hash algorithms +// that the code advertises as supported in a TLS 1.2 ClientHello. +var supportedSKXSignatureAlgorithms = []signatureAndHash{ + {hashSHA256, signatureRSA}, + {hashSHA256, signatureECDSA}, + {hashSHA1, signatureRSA}, + {hashSHA1, signatureECDSA}, +} + +// supportedClientCertSignatureAlgorithms contains the signature and hash +// algorithms that the code advertises as supported in a TLS 1.2 +// CertificateRequest. +var supportedClientCertSignatureAlgorithms = []signatureAndHash{ + {hashSHA256, signatureRSA}, + {hashSHA256, signatureECDSA}, +} + // ConnectionState records basic TLS details about the connection. type ConnectionState struct { - HandshakeComplete bool - DidResume bool - CipherSuite uint16 - NegotiatedProtocol string - NegotiatedProtocolIsMutual bool - - // ServerName contains the server name indicated by the client, if any. - // (Only valid for server connections.) - ServerName string - - // the certificate chain that was presented by the other side - PeerCertificates []*x509.Certificate - // the verified certificate chains built from PeerCertificates. - VerifiedChains [][]*x509.Certificate + 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, ...) + NegotiatedProtocol string // negotiated next protocol (from Config.NextProtos) + NegotiatedProtocolIsMutual bool // negotiated protocol was advertised by server + ServerName string // server name requested by client, if any (server side only) + PeerCertificates []*x509.Certificate // certificate chain presented by remote peer + VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates } // ClientAuthType declares the policy the server will follow for @@ -204,6 +245,15 @@ type Config struct { // connections using that key are compromised. SessionTicketKey [32]byte + // MinVersion contains the minimum SSL/TLS version that is acceptable. + // If zero, then SSLv3 is taken as the minimum. + MinVersion uint16 + + // MaxVersion contains the maximum SSL/TLS version that is acceptable. + // If zero, then the maximum version supported by this package is used, + // which is currently TLS 1.2. + MaxVersion uint16 + serverInitOnce sync.Once // guards calling (*Config).serverInit } @@ -248,6 +298,35 @@ func (c *Config) cipherSuites() []uint16 { return s } +func (c *Config) minVersion() uint16 { + if c == nil || c.MinVersion == 0 { + return minVersion + } + return c.MinVersion +} + +func (c *Config) maxVersion() uint16 { + if c == nil || c.MaxVersion == 0 { + return maxVersion + } + return c.MaxVersion +} + +// mutualVersion returns the protocol version to use given the advertised +// version of the peer. +func (c *Config) mutualVersion(vers uint16) (uint16, bool) { + minVersion := c.minVersion() + maxVersion := c.maxVersion() + + if vers < minVersion { + return 0, false + } + if vers > maxVersion { + vers = maxVersion + } + return vers, true +} + // getCertificateForName returns the best certificate for the given name, // defaulting to the first element of c.Certificates if there are no good // options. @@ -304,7 +383,7 @@ func (c *Config) BuildNameToCertificate() { // A Certificate is a chain of one or more certificates, leaf first. type Certificate struct { Certificate [][]byte - PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey + PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey, *ecdsa.PrivateKey // OCSPStaple contains an optional OCSP response which will be served // to clients that request it. OCSPStaple []byte @@ -327,18 +406,13 @@ type handshakeMessage interface { unmarshal([]byte) bool } -// mutualVersion returns the protocol version to use given the advertised -// version of the peer. -func mutualVersion(vers uint16) (uint16, bool) { - if vers < minVersion { - return 0, false - } - if vers > maxVersion { - vers = maxVersion - } - return vers, true +// TODO(jsing): Make these available to both crypto/x509 and crypto/tls. +type dsaSignature struct { + R, S *big.Int } +type ecdsaSignature dsaSignature + var emptyConfig Config func defaultConfig() *Config { diff --git a/libgo/go/crypto/tls/conn.go b/libgo/go/crypto/tls/conn.go index d8c2be0..2e64b88 100644 --- a/libgo/go/crypto/tls/conn.go +++ b/libgo/go/crypto/tls/conn.go @@ -146,6 +146,9 @@ func (hc *halfConn) changeCipherSpec() error { hc.mac = hc.nextMac hc.nextCipher = nil hc.nextMac = nil + for i := range hc.seq { + hc.seq[i] = 0 + } return nil } @@ -229,8 +232,16 @@ func roundUp(a, b int) int { return a + (b-a%b)%b } -// decrypt checks and strips the mac and decrypts the data in b. -func (hc *halfConn) decrypt(b *block) (bool, alert) { +// cbcMode is an interface for block ciphers using cipher block chaining. +type cbcMode interface { + cipher.BlockMode + SetIV([]byte) +} + +// decrypt checks and strips the mac and decrypts the data in b. Returns a +// success boolean, the number of bytes to skip from the start of the record in +// order to get the application payload, and an optional alert value. +func (hc *halfConn) decrypt(b *block) (ok bool, prefixLen int, alertValue alert) { // pull out payload payload := b.data[recordHeaderLen:] @@ -240,26 +251,54 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { } paddingGood := byte(255) + explicitIVLen := 0 // decrypt if hc.cipher != nil { switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) - case cipher.BlockMode: + case cipher.AEAD: + explicitIVLen = 8 + if len(payload) < explicitIVLen { + return false, 0, alertBadRecordMAC + } + nonce := payload[:8] + payload = payload[8:] + + var additionalData [13]byte + copy(additionalData[:], hc.seq[:]) + copy(additionalData[8:], b.data[:3]) + n := len(payload) - c.Overhead() + additionalData[11] = byte(n >> 8) + additionalData[12] = byte(n) + var err error + payload, err = c.Open(payload[:0], nonce, payload, additionalData[:]) + if err != nil { + return false, 0, alertBadRecordMAC + } + b.resize(recordHeaderLen + explicitIVLen + len(payload)) + case cbcMode: blockSize := c.BlockSize() + if hc.version >= VersionTLS11 { + explicitIVLen = blockSize + } - if len(payload)%blockSize != 0 || len(payload) < roundUp(macSize+1, blockSize) { - return false, alertBadRecordMAC + if len(payload)%blockSize != 0 || len(payload) < roundUp(explicitIVLen+macSize+1, blockSize) { + return false, 0, alertBadRecordMAC } + if explicitIVLen > 0 { + c.SetIV(payload[:explicitIVLen]) + payload = payload[explicitIVLen:] + } c.CryptBlocks(payload, payload) - if hc.version == versionSSL30 { + if hc.version == VersionSSL30 { payload, paddingGood = removePaddingSSL30(payload) } else { payload, paddingGood = removePadding(payload) } - b.resize(recordHeaderLen + len(payload)) + b.resize(recordHeaderLen + explicitIVLen + len(payload)) // note that we still have a timing side-channel in the // MAC check, below. An attacker can align the record @@ -279,25 +318,25 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) { // check, strip mac if hc.mac != nil { if len(payload) < macSize { - return false, alertBadRecordMAC + return false, 0, alertBadRecordMAC } // strip mac off payload, b.data n := len(payload) - macSize b.data[3] = byte(n >> 8) b.data[4] = byte(n) - b.resize(recordHeaderLen + n) + b.resize(recordHeaderLen + explicitIVLen + n) remoteMAC := payload[n:] - localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data) - hc.incSeq() + localMAC := hc.mac.MAC(hc.inDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], payload[:n]) if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 { - return false, alertBadRecordMAC + return false, 0, alertBadRecordMAC } hc.inDigestBuf = localMAC } + hc.incSeq() - return true, 0 + return true, recordHeaderLen + explicitIVLen, 0 } // padToBlockSize calculates the needed padding block, if any, for a payload. @@ -318,11 +357,10 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) { } // encrypt encrypts and macs the data in b. -func (hc *halfConn) encrypt(b *block) (bool, alert) { +func (hc *halfConn) encrypt(b *block, explicitIVLen int) (bool, alert) { // mac if hc.mac != nil { - mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data) - hc.incSeq() + mac := hc.mac.MAC(hc.outDigestBuf, hc.seq[0:], b.data[:recordHeaderLen], b.data[recordHeaderLen+explicitIVLen:]) n := len(b.data) b.resize(n + len(mac)) @@ -337,11 +375,30 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) { switch c := hc.cipher.(type) { case cipher.Stream: c.XORKeyStream(payload, payload) - case cipher.BlockMode: - prefix, finalBlock := padToBlockSize(payload, c.BlockSize()) - b.resize(recordHeaderLen + len(prefix) + len(finalBlock)) - c.CryptBlocks(b.data[recordHeaderLen:], prefix) - c.CryptBlocks(b.data[recordHeaderLen+len(prefix):], finalBlock) + case cipher.AEAD: + payloadLen := len(b.data) - recordHeaderLen - explicitIVLen + b.resize(len(b.data) + c.Overhead()) + nonce := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + payload := b.data[recordHeaderLen+explicitIVLen:] + payload = payload[:payloadLen] + + var additionalData [13]byte + copy(additionalData[:], hc.seq[:]) + copy(additionalData[8:], b.data[:3]) + additionalData[11] = byte(payloadLen >> 8) + additionalData[12] = byte(payloadLen) + + c.Seal(payload[:0], nonce, payload, additionalData[:]) + case cbcMode: + blockSize := c.BlockSize() + if explicitIVLen > 0 { + c.SetIV(payload[:explicitIVLen]) + payload = payload[explicitIVLen:] + } + prefix, finalBlock := padToBlockSize(payload, blockSize) + b.resize(recordHeaderLen + explicitIVLen + len(prefix) + len(finalBlock)) + c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen:], prefix) + c.CryptBlocks(b.data[recordHeaderLen+explicitIVLen+len(prefix):], finalBlock) default: panic("unknown cipher type") } @@ -351,6 +408,7 @@ func (hc *halfConn) encrypt(b *block) (bool, alert) { n := len(b.data) - recordHeaderLen b.data[3] = byte(n >> 8) b.data[4] = byte(n) + hc.incSeq() return true, 0 } @@ -534,10 +592,11 @@ Again: // Process message. b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n) - b.off = recordHeaderLen - if ok, err := c.in.decrypt(b); !ok { + ok, off, err := c.in.decrypt(b) + if !ok { return c.sendAlert(err) } + b.off = off data := b.data[b.off:] if len(data) > maxPlaintext { c.sendAlert(alertRecordOverflow) @@ -637,18 +696,52 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) { if m > maxPlaintext { m = maxPlaintext } - b.resize(recordHeaderLen + m) + explicitIVLen := 0 + explicitIVIsSeq := false + + var cbc cbcMode + if c.out.version >= VersionTLS11 { + var ok bool + if cbc, ok = c.out.cipher.(cbcMode); ok { + explicitIVLen = cbc.BlockSize() + } + } + if explicitIVLen == 0 { + if _, ok := c.out.cipher.(cipher.AEAD); ok { + explicitIVLen = 8 + // The AES-GCM construction in TLS has an + // explicit nonce so that the nonce can be + // random. However, the nonce is only 8 bytes + // which is too small for a secure, random + // nonce. Therefore we use the sequence number + // as the nonce. + explicitIVIsSeq = true + } + } + b.resize(recordHeaderLen + explicitIVLen + m) b.data[0] = byte(typ) vers := c.vers if vers == 0 { - vers = maxVersion + // Some TLS servers fail if the record version is + // greater than TLS 1.0 for the initial ClientHello. + vers = VersionTLS10 } b.data[1] = byte(vers >> 8) b.data[2] = byte(vers) b.data[3] = byte(m >> 8) b.data[4] = byte(m) - copy(b.data[recordHeaderLen:], data) - c.out.encrypt(b) + if explicitIVLen > 0 { + explicitIV := b.data[recordHeaderLen : recordHeaderLen+explicitIVLen] + if explicitIVIsSeq { + copy(explicitIV, c.out.seq[:]) + } else { + if _, err = io.ReadFull(c.config.rand(), explicitIV); err != nil { + break + } + } + } + copy(b.data[recordHeaderLen+explicitIVLen:], data) + c.out.encrypt(b, explicitIVLen) _, err = c.conn.Write(b.data) if err != nil { break @@ -709,7 +802,9 @@ func (c *Conn) readHandshake() (interface{}, error) { case typeCertificate: m = new(certificateMsg) case typeCertificateRequest: - m = new(certificateRequestMsg) + m = &certificateRequestMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } case typeCertificateStatus: m = new(certificateStatusMsg) case typeServerKeyExchange: @@ -719,7 +814,9 @@ func (c *Conn) readHandshake() (interface{}, error) { case typeClientKeyExchange: m = new(clientKeyExchangeMsg) case typeCertificateVerify: - m = new(certificateVerifyMsg) + m = &certificateVerifyMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } case typeNextProtocol: m = new(nextProtoMsg) case typeFinished: @@ -768,7 +865,7 @@ func (c *Conn) Write(b []byte) (int, error) { // http://www.imperialviolet.org/2012/01/15/beastfollowup.html var m int - if len(b) > 1 && c.vers <= versionTLS10 { + if len(b) > 1 && c.vers <= VersionTLS10 { if _, ok := c.out.cipher.(cipher.BlockMode); ok { n, err := c.writeRecord(recordTypeApplicationData, b[:1]) if err != nil { @@ -792,21 +889,32 @@ func (c *Conn) Read(b []byte) (n int, err error) { c.in.Lock() defer c.in.Unlock() - for c.input == nil && c.error() == nil { - if err := c.readRecord(recordTypeApplicationData); err != nil { - // Soft error, like EAGAIN + // Some OpenSSL servers send empty records in order to randomize the + // 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 { + if err := c.readRecord(recordTypeApplicationData); err != nil { + // Soft error, like EAGAIN + return 0, err + } + } + if err := c.error(); err != nil { return 0, err } + + n, err = c.input.Read(b) + if c.input.off >= len(c.input.data) { + c.in.freeBlock(c.input) + c.input = nil + } + + if n != 0 || err != nil { + return n, err + } } - if err := c.error(); err != nil { - return 0, err - } - n, err = c.input.Read(b) - if c.input.off >= len(c.input.data) { - c.in.freeBlock(c.input) - c.input = nil - } - return n, nil + + return 0, io.ErrNoProgress } // Close closes the connection. diff --git a/libgo/go/crypto/tls/generate_cert.go b/libgo/go/crypto/tls/generate_cert.go index 215644d..b417ea4 100644 --- a/libgo/go/crypto/tls/generate_cert.go +++ b/libgo/go/crypto/tls/generate_cert.go @@ -30,7 +30,7 @@ var ( validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011") validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for") isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority") - rsaBits = flag.Int("rsa-bits", 1024, "Size of RSA key to generate") + rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate") ) func main() { diff --git a/libgo/go/crypto/tls/handshake_client.go b/libgo/go/crypto/tls/handshake_client.go index 7db13bf..85e4ade 100644 --- a/libgo/go/crypto/tls/handshake_client.go +++ b/libgo/go/crypto/tls/handshake_client.go @@ -6,25 +6,23 @@ package tls import ( "bytes" - "crypto" + "crypto/ecdsa" "crypto/rsa" "crypto/subtle" "crypto/x509" + "encoding/asn1" "errors" "io" "strconv" ) func (c *Conn) clientHandshake() error { - finishedHash := newFinishedHash(versionTLS10) - if c.config == nil { c.config = defaultConfig() } hello := &clientHelloMsg{ - vers: maxVersion, - cipherSuites: c.config.cipherSuites(), + vers: c.config.maxVersion(), compressionMethods: []uint8{compressionNone}, random: make([]byte, 32), ocspStapling: true, @@ -34,6 +32,25 @@ func (c *Conn) clientHandshake() error { nextProtoNeg: len(c.config.NextProtos) > 0, } + possibleCipherSuites := c.config.cipherSuites() + hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites)) + +NextCipherSuite: + for _, suiteId := range possibleCipherSuites { + for _, suite := range cipherSuites { + if suite.id != suiteId { + continue + } + // Don't advertise TLS 1.2-only cipher suites unless + // we're attempting TLS 1.2. + if hello.vers < VersionTLS12 && suite.flags&suiteTLS12 != 0 { + continue + } + hello.cipherSuites = append(hello.cipherSuites, suiteId) + continue NextCipherSuite + } + } + t := uint32(c.config.time().Unix()) hello.random[0] = byte(t >> 24) hello.random[1] = byte(t >> 16) @@ -45,7 +62,10 @@ func (c *Conn) clientHandshake() error { return errors.New("short read from Rand") } - finishedHash.Write(hello.marshal()) + if hello.vers >= VersionTLS12 { + hello.signatureAndHashes = supportedSKXSignatureAlgorithms + } + c.writeRecord(recordTypeHandshake, hello.marshal()) msg, err := c.readHandshake() @@ -56,16 +76,19 @@ func (c *Conn) clientHandshake() error { if !ok { return c.sendAlert(alertUnexpectedMessage) } - finishedHash.Write(serverHello.marshal()) - vers, ok := mutualVersion(serverHello.vers) - if !ok || vers < versionTLS10 { + vers, ok := c.config.mutualVersion(serverHello.vers) + if !ok || vers < VersionTLS10 { // TLS 1.0 is the minimum version supported as a client. return c.sendAlert(alertProtocolVersion) } c.vers = vers c.haveVers = true + finishedHash := newFinishedHash(c.vers) + finishedHash.Write(hello.marshal()) + finishedHash.Write(serverHello.marshal()) + if serverHello.compressionMethod != compressionNone { return c.sendAlert(alertUnexpectedMessage) } @@ -121,7 +144,10 @@ func (c *Conn) clientHandshake() error { } } - if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok { + switch certs[0].PublicKey.(type) { + case *rsa.PublicKey, *ecdsa.PublicKey: + break + default: return c.sendAlert(alertUnsupportedCertificate) } @@ -148,7 +174,7 @@ func (c *Conn) clientHandshake() error { return err } - keyAgreement := suite.ka() + keyAgreement := suite.ka(c.vers) skx, ok := msg.(*serverKeyExchangeMsg) if ok { @@ -165,7 +191,7 @@ func (c *Conn) clientHandshake() error { } } - var certToSend *Certificate + var chainToSend *Certificate var certRequested bool certReq, ok := msg.(*certificateRequestMsg) if ok { @@ -184,12 +210,13 @@ func (c *Conn) clientHandshake() error { finishedHash.Write(certReq.marshal()) - // For now, we only know how to sign challenges with RSA - rsaAvail := false + var rsaAvail, ecdsaAvail bool for _, certType := range certReq.certificateTypes { - if certType == certTypeRSASign { + switch certType { + case certTypeRSASign: rsaAvail = true - break + case certTypeECDSASign: + ecdsaAvail = true } } @@ -197,35 +224,42 @@ func (c *Conn) clientHandshake() error { // where SignatureAlgorithm is RSA and the Issuer is in // certReq.certificateAuthorities findCert: - for i, cert := range c.config.Certificates { - if !rsaAvail { + for i, chain := range c.config.Certificates { + if !rsaAvail && !ecdsaAvail { continue } - leaf := cert.Leaf - if leaf == nil { - if leaf, err = x509.ParseCertificate(cert.Certificate[0]); err != nil { - c.sendAlert(alertInternalError) - return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + for j, cert := range chain.Certificate { + x509Cert := chain.Leaf + // parse the certificate if this isn't the leaf + // node, or if chain.Leaf was nil + if j != 0 || x509Cert == nil { + if x509Cert, err = x509.ParseCertificate(cert); err != nil { + c.sendAlert(alertInternalError) + return errors.New("tls: failed to parse client certificate #" + strconv.Itoa(i) + ": " + err.Error()) + } } - } - - if leaf.PublicKeyAlgorithm != x509.RSA { - continue - } - if len(certReq.certificateAuthorities) == 0 { - // they gave us an empty list, so just take the - // first RSA cert from c.config.Certificates - certToSend = &cert - break - } + switch { + case rsaAvail && x509Cert.PublicKeyAlgorithm == x509.RSA: + case ecdsaAvail && x509Cert.PublicKeyAlgorithm == x509.ECDSA: + default: + continue findCert + } - for _, ca := range certReq.certificateAuthorities { - if bytes.Equal(leaf.RawIssuer, ca) { - certToSend = &cert + if len(certReq.certificateAuthorities) == 0 { + // they gave us an empty list, so just take the + // first RSA cert from c.config.Certificates + chainToSend = &chain break findCert } + + for _, ca := range certReq.certificateAuthorities { + if bytes.Equal(x509Cert.RawIssuer, ca) { + chainToSend = &chain + break findCert + } + } } } @@ -246,8 +280,8 @@ func (c *Conn) clientHandshake() error { // certificate to send. if certRequested { certMsg = new(certificateMsg) - if certToSend != nil { - certMsg.certificates = certToSend.Certificate + if chainToSend != nil { + certMsg.certificates = chainToSend.Certificate } finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) @@ -263,12 +297,29 @@ func (c *Conn) clientHandshake() error { c.writeRecord(recordTypeHandshake, ckx.marshal()) } - if certToSend != nil { - certVerify := new(certificateVerifyMsg) - digest := make([]byte, 0, 36) - digest = finishedHash.serverMD5.Sum(digest) - digest = finishedHash.serverSHA1.Sum(digest) - signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest) + if chainToSend != nil { + var signed []byte + certVerify := &certificateVerifyMsg{ + hasSignatureAndHash: c.vers >= VersionTLS12, + } + + switch key := c.config.Certificates[0].PrivateKey.(type) { + case *ecdsa.PrivateKey: + digest, _, hashId := finishedHash.hashForClientCertificate(signatureECDSA) + r, s, err := ecdsa.Sign(c.config.rand(), key, digest) + if err == nil { + signed, err = asn1.Marshal(ecdsaSignature{r, s}) + } + certVerify.signatureAndHash.signature = signatureECDSA + certVerify.signatureAndHash.hash = hashId + case *rsa.PrivateKey: + digest, hashFunc, hashId := finishedHash.hashForClientCertificate(signatureRSA) + signed, err = rsa.SignPKCS1v15(c.config.rand(), key, hashFunc, digest) + certVerify.signatureAndHash.signature = signatureRSA + certVerify.signatureAndHash.hash = hashId + default: + err = errors.New("unknown private key type") + } if err != nil { return c.sendAlert(alertInternalError) } @@ -282,8 +333,14 @@ func (c *Conn) clientHandshake() error { clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromMasterSecret(c.vers, masterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen) - clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */) - clientHash := suite.mac(c.vers, clientMAC) + var clientCipher interface{} + var clientHash macFunction + if suite.cipher != nil { + clientCipher = suite.cipher(clientKey, clientIV, false /* not for reading */) + clientHash = suite.mac(c.vers, clientMAC) + } else { + clientCipher = suite.aead(clientKey, clientIV) + } c.out.prepareCipherSpec(c.vers, clientCipher, clientHash) c.writeRecord(recordTypeChangeCipherSpec, []byte{1}) @@ -303,8 +360,14 @@ func (c *Conn) clientHandshake() error { finishedHash.Write(finished.marshal()) c.writeRecord(recordTypeHandshake, finished.marshal()) - serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */) - serverHash := suite.mac(c.vers, serverMAC) + var serverCipher interface{} + var serverHash macFunction + if suite.cipher != nil { + serverCipher = suite.cipher(serverKey, serverIV, true /* for reading */) + serverHash = suite.mac(c.vers, serverMAC) + } else { + serverCipher = suite.aead(serverKey, serverIV) + } c.in.prepareCipherSpec(c.vers, serverCipher, serverHash) c.readRecord(recordTypeChangeCipherSpec) if err := c.error(); err != nil { diff --git a/libgo/go/crypto/tls/handshake_client_test.go b/libgo/go/crypto/tls/handshake_client_test.go index 9673947..6c56400 100644 --- a/libgo/go/crypto/tls/handshake_client_test.go +++ b/libgo/go/crypto/tls/handshake_client_test.go @@ -39,16 +39,56 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config * } } -func TestHandshakeClientRC4(t *testing.T) { +func TestHandshakeClientRSARC4(t *testing.T) { var config = *testConfig config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA} - testClientScript(t, "RC4", rc4ClientScript, &config) + testClientScript(t, "RSA-RC4", rsaRC4ClientScript, &config) } -func TestHandshakeClientECDHEAES(t *testing.T) { +func TestHandshakeClientECDHERSAAES(t *testing.T) { var config = *testConfig config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA} - testClientScript(t, "ECDHE-AES", ecdheAESClientScript, &config) + testClientScript(t, "ECDHE-RSA-AES", ecdheRSAAESClientScript, &config) +} + +func TestHandshakeClientECDHECDSAAES(t *testing.T) { + var config = *testConfig + config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA} + config.Certificates = nil + config.BuildNameToCertificate() + testClientScript(t, "ECDHE-ECDSA-AES", ecdheECDSAAESClientScript, &config) +} + +func TestLongClientCerticiateChain(t *testing.T) { + config := *testConfig + cert, _ := X509KeyPair(testClientChainCertificate, testClientChainCertificate) + config.Certificates = []Certificate{cert} + testClientScript(t, "Long client certificate chains", clientChainCertificateScript, &config) +} + +func TestHandshakeClientTLS11(t *testing.T) { + var config = *testConfig + config.MaxVersion = VersionTLS11 + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA} + testClientScript(t, "TLS11-ECDHE-AES", tls11ECDHEAESClientScript, &config) +} + +func TestHandshakeClientTLS12(t *testing.T) { + config := *testConfig + config.MaxVersion = VersionTLS12 + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA} + cert, _ := X509KeyPair(testClientChainCertificate, testClientChainCertificate) + config.Certificates = []Certificate{cert} + testClientScript(t, "TLS12", clientTLS12Script, &config) +} + +func TestHandshakeClientTLS12ClientCert(t *testing.T) { + config := *testConfig + config.MaxVersion = VersionTLS12 + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} + cert, _ := X509KeyPair(testClientChainCertificate, testClientChainCertificate) + config.Certificates = []Certificate{cert} + testClientScript(t, "TLS12ClientCert", clientTLS12ClientCertScript, &config) } var connect = flag.Bool("connect", false, "connect to a TLS server on :10443") @@ -79,6 +119,48 @@ func TestRunClient(t *testing.T) { record.WriteTo(os.Stdout) } +func TestEmptyRecords(t *testing.T) { + // emptyRecordScript contains a TLS connection with an empty record as + // the first application data from the server. This test ensures that + // the empty record doesn't cause (0, nil) to be returned from + // Conn.Read. + config := *testConfig + config.CipherSuites = []uint16{TLS_RSA_WITH_AES_256_CBC_SHA} + + c, s := net.Pipe() + cli := Client(c, &config) + go func() { + buf := make([]byte, 1024) + n, err := cli.Read(buf) + defer c.Close() + defer cli.Close() + + if err != nil { + t.Fatalf("error reading from tls.Client: %s", err) + } + const expectedLength = 197 + if n != expectedLength { + t.Fatalf("incorrect length reading from tls.Client, got %d, want %d", n, expectedLength) + } + }() + + defer c.Close() + for i, b := range emptyRecordScript { + if i%2 == 1 { + s.Write(b) + continue + } + bb := make([]byte, len(b)) + _, err := io.ReadFull(s, bb) + if err != nil { + t.Fatalf("#%d: %s", i, err) + } + if !bytes.Equal(b, bb) { + t.Fatalf("#%d: mismatch on read: got:%x want:%x", i, bb, b) + } + } +} + // Script of interaction with gnutls implementation. // The values for this test are obtained by building and running in client mode: // % go test -test.run "TestRunClient" -connect @@ -110,7 +192,7 @@ func TestRunClient(t *testing.T) { // CSqGSIb3DQEBBQUAA0EAhTZAc8G7GtrUWZ8tonAxRnTsg26oyDxRrzms7EC86CJG // HZnWRiok1IsFCEv7NRFukrt3uuQSu/TIXpyBqJdgTA== // -----END CERTIFICATE----- -var rc4ClientScript = [][]byte{ +var rsaRC4ClientScript = [][]byte{ { 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -231,7 +313,7 @@ var rc4ClientScript = [][]byte{ }, } -var ecdheAESClientScript = [][]byte{ +var ecdheRSAAESClientScript = [][]byte{ { 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -406,3 +488,2563 @@ var ecdheAESClientScript = [][]byte{ 0x57, 0x33, 0xc3, 0xbc, 0x3f, 0x7a, 0x4d, }, } + +var emptyRecordScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x35, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x51, 0x71, 0x8e, 0x03, 0x02, + 0xef, 0x09, 0xf2, 0x0e, 0xf5, 0x3b, 0x29, 0x9a, + 0xa8, 0x8b, 0x46, 0xa3, 0xd4, 0xb4, 0xc1, 0x14, + 0xc3, 0x19, 0x99, 0xba, 0x3d, 0x78, 0xcf, 0x50, + 0xd1, 0xe7, 0x26, 0x20, 0xa0, 0x37, 0x6d, 0xc9, + 0xae, 0x93, 0x33, 0x81, 0x20, 0xe3, 0xc1, 0x90, + 0x64, 0x6e, 0x67, 0x93, 0xdb, 0xb4, 0x04, 0x16, + 0xc4, 0x25, 0xdd, 0x10, 0x79, 0x3c, 0x18, 0x0a, + 0x7c, 0xfd, 0x28, 0x65, 0x00, 0x35, 0x00, 0x16, + 0x03, 0x01, 0x09, 0x9e, 0x0b, 0x00, 0x09, 0x9a, + 0x00, 0x09, 0x97, 0x00, 0x04, 0xea, 0x30, 0x82, + 0x04, 0xe6, 0x30, 0x82, 0x03, 0xce, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x11, 0x00, 0xff, 0xab, + 0x02, 0x93, 0xe0, 0x72, 0x99, 0x18, 0x6c, 0x9e, + 0x96, 0xb8, 0xb9, 0xf7, 0x47, 0xcb, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x41, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x46, 0x52, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x09, 0x47, + 0x41, 0x4e, 0x44, 0x49, 0x20, 0x53, 0x41, 0x53, + 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x15, 0x47, 0x61, 0x6e, 0x64, 0x69, + 0x20, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, + 0x64, 0x20, 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, + 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x33, 0x30, 0x31, + 0x31, 0x34, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x31, 0x31, + 0x34, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, + 0x30, 0x62, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x13, 0x18, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x20, 0x43, 0x6f, 0x6e, 0x74, + 0x72, 0x6f, 0x6c, 0x20, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x31, 0x24, 0x30, + 0x22, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x1b, + 0x47, 0x61, 0x6e, 0x64, 0x69, 0x20, 0x53, 0x74, + 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, 0x57, + 0x69, 0x6c, 0x64, 0x63, 0x61, 0x72, 0x64, 0x20, + 0x53, 0x53, 0x4c, 0x31, 0x17, 0x30, 0x15, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x14, 0x0e, 0x2a, 0x2e, + 0x66, 0x72, 0x65, 0x65, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xdc, 0xe3, 0xfd, + 0xce, 0xc1, 0x66, 0x62, 0x28, 0x8b, 0x99, 0x65, + 0x72, 0x52, 0x88, 0x93, 0x5b, 0x3f, 0x8d, 0xde, + 0x2b, 0xb0, 0xa0, 0xf4, 0xbd, 0xb4, 0x07, 0x5f, + 0x9e, 0x01, 0x47, 0x60, 0x57, 0x5f, 0xdf, 0xdc, + 0x63, 0x28, 0x1c, 0x1e, 0x5b, 0xc8, 0xe6, 0x29, + 0xdd, 0xeb, 0x26, 0x63, 0xd5, 0xbf, 0x83, 0xb2, + 0x2d, 0xcd, 0x2c, 0xa0, 0xb6, 0x91, 0xad, 0xaf, + 0x95, 0x21, 0x1d, 0x1f, 0x39, 0x8d, 0x3e, 0x17, + 0xd6, 0xbd, 0x99, 0xf5, 0x6c, 0xd4, 0xcb, 0x79, + 0x12, 0x3e, 0x11, 0xb9, 0x7e, 0x62, 0xbc, 0x2d, + 0xbf, 0xe0, 0x55, 0x1b, 0x5c, 0x1e, 0xce, 0x31, + 0xd9, 0xf8, 0x56, 0x68, 0x95, 0x2b, 0x15, 0x84, + 0x35, 0xae, 0x98, 0x2c, 0x63, 0x01, 0xb2, 0x0d, + 0xab, 0xa8, 0x61, 0xef, 0x7f, 0x15, 0x2c, 0x6d, + 0xf7, 0x67, 0x1d, 0xb8, 0x8d, 0xf6, 0xa2, 0x1c, + 0x4e, 0x85, 0xf0, 0xea, 0x1a, 0x2b, 0xc8, 0xac, + 0x70, 0x86, 0x9a, 0xbb, 0x9e, 0x9d, 0xbd, 0xc9, + 0x87, 0x2b, 0x9f, 0x5e, 0x40, 0x44, 0x9b, 0xba, + 0x96, 0x45, 0x24, 0xbc, 0x49, 0xb8, 0xfe, 0x26, + 0x3a, 0x1d, 0x1a, 0x0a, 0x3a, 0x90, 0x9c, 0x75, + 0x51, 0x59, 0x89, 0x98, 0x1a, 0x56, 0xe1, 0x3a, + 0x1a, 0xba, 0xff, 0xb4, 0x37, 0x7d, 0xd8, 0x99, + 0xe2, 0xeb, 0x45, 0x27, 0xe2, 0x42, 0x42, 0x46, + 0xbb, 0x00, 0x29, 0x9f, 0x30, 0xc9, 0x1e, 0x6c, + 0xce, 0x59, 0x0e, 0xbe, 0x16, 0x03, 0x31, 0xec, + 0x10, 0xc1, 0x6d, 0xca, 0x9d, 0x5f, 0x6d, 0xf1, + 0x26, 0x11, 0xe5, 0x50, 0xa1, 0xbb, 0x67, 0xb2, + 0xe0, 0x2b, 0xed, 0x76, 0x5b, 0xc7, 0x68, 0xc0, + 0x18, 0xad, 0x91, 0x9e, 0xb5, 0xd4, 0x4d, 0x21, + 0xcd, 0x98, 0xd9, 0xe0, 0x05, 0x0a, 0x4d, 0x24, + 0xa3, 0xe6, 0x12, 0x04, 0xdd, 0x50, 0xe6, 0xc8, + 0x7a, 0x69, 0xb9, 0x32, 0x43, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x01, 0xb6, 0x30, 0x82, + 0x01, 0xb2, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb6, + 0xa8, 0xff, 0xa2, 0xa8, 0x2f, 0xd0, 0xa6, 0xcd, + 0x4b, 0xb1, 0x68, 0xf3, 0xe7, 0x50, 0x10, 0x31, + 0xa7, 0x79, 0x21, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x62, 0x37, + 0xd4, 0x3c, 0xbf, 0xd9, 0xc2, 0x99, 0xf3, 0x28, + 0x3e, 0xdb, 0xca, 0xee, 0xf3, 0xb3, 0xc8, 0x73, + 0xb0, 0x3c, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, + 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x05, 0xa0, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, + 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, + 0x60, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x59, + 0x30, 0x57, 0x30, 0x4b, 0x06, 0x0b, 0x2b, 0x06, + 0x01, 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, 0x02, + 0x1a, 0x30, 0x3c, 0x30, 0x3a, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, + 0x2e, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x77, 0x77, 0x77, 0x2e, 0x67, 0x61, 0x6e, 0x64, + 0x69, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, + 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x73, 0x2f, + 0x66, 0x72, 0x2f, 0x73, 0x73, 0x6c, 0x2f, 0x63, + 0x70, 0x73, 0x2f, 0x70, 0x64, 0x66, 0x2f, 0x30, + 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, + 0x01, 0x30, 0x3c, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x35, 0x30, 0x33, 0x30, 0x31, 0xa0, 0x2f, + 0xa0, 0x2d, 0x86, 0x2b, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, 0x67, + 0x61, 0x6e, 0x64, 0x69, 0x2e, 0x6e, 0x65, 0x74, + 0x2f, 0x47, 0x61, 0x6e, 0x64, 0x69, 0x53, 0x74, + 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x53, 0x53, + 0x4c, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, + 0x6a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x5e, 0x30, 0x5c, 0x30, + 0x37, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x02, 0x86, 0x2b, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, 0x2e, + 0x67, 0x61, 0x6e, 0x64, 0x69, 0x2e, 0x6e, 0x65, + 0x74, 0x2f, 0x47, 0x61, 0x6e, 0x64, 0x69, 0x53, + 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x53, + 0x53, 0x4c, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x21, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x01, 0x86, 0x15, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, + 0x70, 0x2e, 0x67, 0x61, 0x6e, 0x64, 0x69, 0x2e, + 0x6e, 0x65, 0x74, 0x30, 0x27, 0x06, 0x03, 0x55, + 0x1d, 0x11, 0x04, 0x20, 0x30, 0x1e, 0x82, 0x0e, + 0x2a, 0x2e, 0x66, 0x72, 0x65, 0x65, 0x6e, 0x6f, + 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x82, 0x0c, + 0x66, 0x72, 0x65, 0x65, 0x6e, 0x6f, 0x64, 0x65, + 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x5b, 0x4a, 0x3a, 0x1d, 0x75, 0xe0, 0xc0, 0x9e, + 0xc9, 0x16, 0x66, 0x7f, 0x73, 0x95, 0x6e, 0x35, + 0xe4, 0x27, 0xfa, 0x8c, 0x9d, 0xee, 0xb1, 0x37, + 0x42, 0x3f, 0x54, 0x6a, 0x9d, 0x41, 0x84, 0x57, + 0xe1, 0x03, 0x3d, 0x69, 0x61, 0x77, 0x3b, 0x91, + 0xa2, 0x70, 0x94, 0xb6, 0x8e, 0x41, 0x63, 0x70, + 0xf2, 0x16, 0x04, 0x50, 0x05, 0x14, 0xfb, 0x59, + 0x7d, 0x89, 0x09, 0x3f, 0xb6, 0xef, 0xca, 0x3c, + 0x89, 0x88, 0x08, 0xe9, 0xa1, 0xf3, 0x33, 0x31, + 0x05, 0x4d, 0x70, 0xff, 0xdd, 0xa7, 0xd2, 0xe2, + 0xa0, 0x94, 0x3a, 0xf7, 0xc2, 0x9f, 0xad, 0x2b, + 0x2e, 0x20, 0xfa, 0x6c, 0xe1, 0xfc, 0xe6, 0x62, + 0x22, 0xa1, 0x38, 0x93, 0xec, 0x3e, 0xce, 0xfd, + 0x1f, 0xdd, 0xd4, 0x7c, 0x39, 0x46, 0x8b, 0xb4, + 0x64, 0xfa, 0xa1, 0x46, 0x87, 0x78, 0x2c, 0xd7, + 0x9c, 0xdd, 0x60, 0xd6, 0xda, 0x8e, 0xd8, 0x29, + 0x6d, 0x61, 0xa7, 0x29, 0x07, 0x76, 0xfc, 0xf9, + 0xbd, 0xfd, 0x14, 0xeb, 0x44, 0x70, 0xff, 0xd0, + 0x23, 0x99, 0x83, 0xc5, 0x5c, 0x56, 0x88, 0xaa, + 0x34, 0xda, 0xa6, 0xb3, 0x9a, 0xbf, 0xda, 0x58, + 0x1e, 0xa4, 0xb8, 0xc0, 0x40, 0x9d, 0xf0, 0xfc, + 0xf1, 0x23, 0xc2, 0xbc, 0x59, 0xe1, 0x82, 0xed, + 0x5d, 0xfb, 0x99, 0xaf, 0xf5, 0xf5, 0x15, 0xb8, + 0x8b, 0x59, 0xce, 0xaa, 0xca, 0xdf, 0xdc, 0x94, + 0x11, 0xe0, 0x96, 0xbf, 0x9f, 0x54, 0xa4, 0x9f, + 0x54, 0x36, 0x4a, 0xe8, 0x93, 0xda, 0xf4, 0x8c, + 0xb0, 0x6b, 0x8d, 0x4a, 0x9e, 0x11, 0xae, 0xcb, + 0xcb, 0x33, 0x8a, 0x4d, 0xcd, 0x4e, 0xa5, 0x9b, + 0xe9, 0x14, 0x46, 0x43, 0x9b, 0x96, 0x5f, 0x6d, + 0xf2, 0xea, 0x40, 0xef, 0x14, 0xc3, 0x99, 0x9f, + 0x23, 0x1e, 0xa5, 0x13, 0xab, 0x08, 0xea, 0x8f, + 0x68, 0x5b, 0x7d, 0x71, 0xdf, 0x18, 0xd1, 0x57, + 0x00, 0x04, 0xa7, 0x30, 0x82, 0x04, 0xa3, 0x30, + 0x82, 0x03, 0x8b, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x10, 0x5a, 0xb6, 0x1d, 0xac, 0x1e, 0x4d, + 0xa2, 0x06, 0x14, 0xc7, 0x55, 0x3d, 0x3d, 0xa9, + 0xb2, 0xdc, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0x97, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x02, 0x55, 0x54, 0x31, 0x17, + 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, + 0x0e, 0x53, 0x61, 0x6c, 0x74, 0x20, 0x4c, 0x61, + 0x6b, 0x65, 0x20, 0x43, 0x69, 0x74, 0x79, 0x31, + 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x15, 0x54, 0x68, 0x65, 0x20, 0x55, 0x53, + 0x45, 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x20, + 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x75, 0x73, 0x65, + 0x72, 0x74, 0x72, 0x75, 0x73, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e, + 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, 0x72, + 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64, 0x77, + 0x61, 0x72, 0x65, 0x30, 0x1e, 0x17, 0x0d, 0x30, + 0x38, 0x31, 0x30, 0x32, 0x33, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x32, 0x30, + 0x30, 0x35, 0x33, 0x30, 0x31, 0x30, 0x34, 0x38, + 0x33, 0x38, 0x5a, 0x30, 0x41, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x46, 0x52, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x09, 0x47, 0x41, 0x4e, + 0x44, 0x49, 0x20, 0x53, 0x41, 0x53, 0x31, 0x1e, + 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x15, 0x47, 0x61, 0x6e, 0x64, 0x69, 0x20, 0x53, + 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x20, + 0x53, 0x53, 0x4c, 0x20, 0x43, 0x41, 0x30, 0x82, + 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, + 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb6, + 0x54, 0x3d, 0xa5, 0xdb, 0x0d, 0x22, 0x78, 0x50, + 0x6a, 0x5a, 0x23, 0x89, 0x3f, 0x97, 0xa1, 0xd4, + 0x07, 0x1a, 0xa9, 0x58, 0x08, 0x9b, 0xa0, 0x15, + 0xc3, 0x32, 0xb6, 0xb7, 0xf1, 0xe8, 0xb9, 0xa5, + 0x6f, 0xad, 0x37, 0xf6, 0x6e, 0x71, 0x1b, 0xb4, + 0x75, 0x2d, 0x48, 0x5e, 0x9f, 0xc6, 0x15, 0xaa, + 0x81, 0xef, 0xe5, 0xc4, 0x88, 0x95, 0x8a, 0x3a, + 0x6c, 0x77, 0xcc, 0xb5, 0xcd, 0x65, 0xe4, 0x67, + 0xe5, 0x73, 0xc9, 0x50, 0x52, 0x94, 0xc1, 0x27, + 0x49, 0x3e, 0xa0, 0x6b, 0x41, 0x16, 0x41, 0xb6, + 0x94, 0x99, 0x41, 0xae, 0x3e, 0xcb, 0xe2, 0x06, + 0x46, 0x09, 0xe9, 0x4d, 0xbe, 0xc9, 0x4c, 0x55, + 0xa9, 0x18, 0x7e, 0xa6, 0xdf, 0x6e, 0xfd, 0x4a, + 0xb2, 0xcc, 0x6c, 0x4e, 0xd9, 0xc8, 0x50, 0x15, + 0x93, 0xb3, 0xf2, 0xe9, 0xe3, 0xc2, 0x6a, 0xad, + 0x3a, 0xd5, 0xfb, 0xc3, 0x79, 0x50, 0x9f, 0x25, + 0x79, 0x29, 0xb2, 0x47, 0x64, 0x7c, 0x20, 0x3e, + 0xe2, 0x08, 0x4d, 0x93, 0x29, 0x14, 0xb6, 0x34, + 0x6e, 0xcf, 0x71, 0x46, 0x7e, 0x76, 0x10, 0xf4, + 0xfd, 0x6c, 0xaa, 0x01, 0xd2, 0xc2, 0x06, 0xde, + 0x92, 0x83, 0xcc, 0x58, 0x90, 0x2e, 0x92, 0xde, + 0x1e, 0x65, 0xb7, 0x63, 0x2f, 0x3d, 0xb2, 0xeb, + 0x70, 0x8c, 0x4c, 0xe0, 0xbe, 0x15, 0x9d, 0xde, + 0xc1, 0x4d, 0x56, 0xf8, 0x0b, 0xc6, 0x8e, 0x07, + 0xb9, 0x5d, 0xdf, 0x95, 0xf0, 0x7b, 0x40, 0x1f, + 0x1a, 0x2c, 0xd7, 0x9c, 0x2b, 0x4b, 0x76, 0xf4, + 0x59, 0xf5, 0x43, 0xc1, 0x2c, 0x66, 0x10, 0x9e, + 0x9e, 0x66, 0x96, 0x60, 0x9d, 0x1c, 0x74, 0x1b, + 0x4e, 0x18, 0x5c, 0x08, 0xb0, 0x6e, 0x6c, 0xca, + 0x69, 0x1a, 0x02, 0xe9, 0xbb, 0xca, 0x78, 0xef, + 0x66, 0x2e, 0xe3, 0x32, 0xfd, 0x41, 0x5c, 0x95, + 0x74, 0x81, 0x4d, 0xf4, 0xda, 0xfe, 0x4b, 0x02, + 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x3e, + 0x30, 0x82, 0x01, 0x3a, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0xa1, 0x72, 0x5f, 0x26, 0x1b, 0x28, 0x98, + 0x43, 0x95, 0x5d, 0x07, 0x37, 0xd5, 0x85, 0x96, + 0x9d, 0x4b, 0xd2, 0xc3, 0x45, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xb6, 0xa8, 0xff, 0xa2, 0xa8, 0x2f, 0xd0, 0xa6, + 0xcd, 0x4b, 0xb1, 0x68, 0xf3, 0xe7, 0x50, 0x10, + 0x31, 0xa7, 0x79, 0x21, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, + 0x03, 0x02, 0x01, 0x06, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, + 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, + 0x30, 0x18, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, + 0x11, 0x30, 0x0f, 0x30, 0x0d, 0x06, 0x0b, 0x2b, + 0x06, 0x01, 0x04, 0x01, 0xb2, 0x31, 0x01, 0x02, + 0x02, 0x1a, 0x30, 0x44, 0x06, 0x03, 0x55, 0x1d, + 0x1f, 0x04, 0x3d, 0x30, 0x3b, 0x30, 0x39, 0xa0, + 0x37, 0xa0, 0x35, 0x86, 0x33, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, 0x2e, + 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, 0x73, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x55, 0x54, + 0x4e, 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x69, + 0x72, 0x73, 0x74, 0x2d, 0x48, 0x61, 0x72, 0x64, + 0x77, 0x61, 0x72, 0x65, 0x2e, 0x63, 0x72, 0x6c, + 0x30, 0x74, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x01, 0x01, 0x04, 0x68, 0x30, 0x66, + 0x30, 0x3d, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x30, 0x02, 0x86, 0x31, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x74, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x55, + 0x54, 0x4e, 0x41, 0x64, 0x64, 0x54, 0x72, 0x75, + 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x5f, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, + 0x25, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x30, 0x01, 0x86, 0x19, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x74, 0x72, 0x75, + 0x73, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x19, 0x53, 0xbf, 0x03, 0x3d, 0x9b, + 0xe2, 0x6b, 0x5a, 0xfd, 0xba, 0x49, 0x1f, 0x4f, + 0xec, 0xe1, 0xc6, 0x82, 0x39, 0x3c, 0xd2, 0x03, + 0x04, 0x0f, 0xab, 0x7b, 0x3e, 0x82, 0xa9, 0x85, + 0x10, 0x1f, 0xf4, 0xde, 0x32, 0xaf, 0x58, 0x3f, + 0xff, 0x70, 0xf3, 0x30, 0x1d, 0x97, 0x2d, 0x4c, + 0x9a, 0xe2, 0xec, 0x0c, 0x3e, 0x14, 0x2d, 0x2f, + 0x98, 0x48, 0x9d, 0xae, 0x16, 0x6a, 0xac, 0x2d, + 0x42, 0xaa, 0xb5, 0x64, 0xa4, 0x70, 0xbb, 0xeb, + 0x73, 0x94, 0x7b, 0x46, 0x4c, 0xe7, 0x7a, 0x14, + 0x76, 0x5b, 0x4c, 0x1d, 0x84, 0xa1, 0x20, 0x74, + 0x1f, 0x2e, 0x4b, 0x5c, 0x70, 0x88, 0xdc, 0xbd, + 0xf7, 0x19, 0x3d, 0xed, 0x59, 0x0d, 0xe2, 0x3f, + 0x26, 0xe2, 0x9c, 0xac, 0xa4, 0x3c, 0x95, 0x1c, + 0xf8, 0xbe, 0x8c, 0x03, 0xae, 0xf0, 0xe5, 0x9c, + 0x4d, 0xbc, 0xc7, 0x9b, 0x58, 0x00, 0xbf, 0xaf, + 0xad, 0xfa, 0x37, 0x6e, 0x71, 0x6d, 0x18, 0x34, + 0x0e, 0xc1, 0xea, 0x6a, 0xf8, 0x0d, 0xdf, 0x69, + 0x54, 0x56, 0x15, 0xf2, 0x28, 0xb3, 0xfe, 0xa4, + 0x63, 0xec, 0xc5, 0x04, 0x64, 0x60, 0xbb, 0xfe, + 0x2a, 0xf0, 0xf4, 0x87, 0xa1, 0xb0, 0xae, 0xbd, + 0xaa, 0xe4, 0x2f, 0xe3, 0x03, 0x0b, 0x2f, 0x66, + 0x5f, 0x85, 0xa4, 0x32, 0x7b, 0x46, 0xed, 0x25, + 0x0c, 0xe7, 0xf1, 0xb7, 0xe7, 0x19, 0xfd, 0x60, + 0xba, 0x5f, 0x87, 0x77, 0xde, 0x98, 0x07, 0x96, + 0xe4, 0x5e, 0xea, 0x63, 0x7d, 0xa8, 0xde, 0x55, + 0xda, 0x61, 0x5c, 0x3c, 0x90, 0x83, 0x43, 0x04, + 0x07, 0x3c, 0xdd, 0xf3, 0xf8, 0x9f, 0x06, 0x52, + 0x0a, 0xde, 0xc7, 0xb6, 0x7b, 0x8f, 0xe1, 0x11, + 0xf7, 0x04, 0x7a, 0x35, 0xff, 0x6a, 0xbc, 0x5b, + 0xc7, 0x50, 0x49, 0x08, 0x70, 0x6f, 0x94, 0x43, + 0xcd, 0x9e, 0xc7, 0x70, 0xf1, 0xdb, 0xd0, 0x6d, + 0xda, 0x8f, 0x16, 0x03, 0x01, 0x00, 0x0e, 0x0d, + 0x00, 0x00, 0x06, 0x03, 0x01, 0x02, 0x40, 0x00, + 0x00, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, + 0xba, 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, + 0x82, 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, + 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, + 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, + 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, + 0x39, 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, + 0x31, 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, + 0x30, 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, + 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, + 0x00, 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, + 0xbf, 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, + 0x2b, 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, + 0x7a, 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, + 0x65, 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, + 0xb4, 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, + 0x62, 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, + 0x5c, 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, + 0x58, 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, + 0xd0, 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, + 0x9f, 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, + 0x18, 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, + 0xf1, 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, + 0xc9, 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, + 0x01, 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, + 0x1d, 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, + 0x79, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, + 0xa7, 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, + 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, + 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, + 0x18, 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, + 0xb1, 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, + 0xdb, 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, + 0x8e, 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, + 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, + 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, + 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, + 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, + 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, + 0xb8, 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, + 0x81, 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, + 0x6b, 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, + 0xb0, 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, + 0xb5, 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, + 0xae, 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, + 0x6e, 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, + 0xb5, 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, + 0x30, 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, + 0xe7, 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, + 0x78, 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, + 0x2d, 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, + 0x75, 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, + 0xcd, 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, + 0x1c, 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, + 0x57, 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, + 0x9b, 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, + 0xa7, 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x01, 0x06, + 0x10, 0x00, 0x01, 0x02, 0x01, 0x00, 0x25, 0x48, + 0x6c, 0x0a, 0xde, 0x9d, 0x3a, 0x57, 0xe4, 0x2e, + 0xb9, 0xfc, 0xb4, 0x46, 0x1f, 0x20, 0x4f, 0x58, + 0x4d, 0x12, 0x08, 0xb4, 0x3e, 0x4c, 0xf5, 0xa8, + 0xa5, 0x16, 0x40, 0x29, 0x19, 0x04, 0x4d, 0xf9, + 0x54, 0x3a, 0x32, 0xd7, 0x79, 0xf2, 0x0e, 0xc1, + 0x7b, 0x0c, 0x62, 0x71, 0xbb, 0xb4, 0x8c, 0xe7, + 0x84, 0xd5, 0xf8, 0x11, 0x77, 0x7f, 0x87, 0x6c, + 0xfc, 0x25, 0xf3, 0x2d, 0x97, 0x3d, 0x1f, 0xf5, + 0xfc, 0x64, 0x94, 0x9f, 0xdd, 0x90, 0x82, 0xdd, + 0x11, 0x74, 0x74, 0x59, 0xa2, 0x1a, 0x71, 0xb2, + 0x55, 0x6d, 0x18, 0xca, 0x85, 0x47, 0x8b, 0x79, + 0x73, 0x06, 0x24, 0x38, 0xc3, 0x34, 0x98, 0x84, + 0x62, 0x81, 0xd8, 0xad, 0x54, 0xad, 0x13, 0xa5, + 0xf4, 0xe4, 0x82, 0x85, 0xd3, 0xe3, 0x9e, 0xeb, + 0xb5, 0xf5, 0x95, 0x83, 0x0e, 0xb9, 0x7d, 0xb6, + 0xda, 0x0c, 0xf6, 0x14, 0x6a, 0x60, 0x8c, 0x75, + 0x56, 0xf0, 0xe9, 0x60, 0xe0, 0x4c, 0xf4, 0x4e, + 0x84, 0x8b, 0x4f, 0xf4, 0x2f, 0xde, 0xb7, 0xec, + 0x61, 0xd3, 0x77, 0x07, 0x6e, 0x41, 0x57, 0xc9, + 0xd9, 0x1d, 0x75, 0xee, 0x42, 0x63, 0xdc, 0x58, + 0xad, 0xfc, 0xc7, 0xe1, 0x77, 0x49, 0xb1, 0x58, + 0x21, 0x96, 0x00, 0x55, 0x90, 0x6b, 0xf6, 0x2a, + 0x5a, 0x19, 0x25, 0x93, 0x59, 0x9d, 0xaf, 0x79, + 0x9b, 0x18, 0x5d, 0xf6, 0x5d, 0x64, 0x4b, 0x9a, + 0xf4, 0xde, 0xf2, 0x7f, 0xbd, 0x93, 0x7e, 0x45, + 0x3e, 0x17, 0xae, 0xbf, 0x52, 0xe1, 0xba, 0x8e, + 0x0b, 0xbc, 0x1e, 0x91, 0x9d, 0xf1, 0x4e, 0x0b, + 0xab, 0x9e, 0x5c, 0x4c, 0x6f, 0xf7, 0xf3, 0x8d, + 0x8c, 0x6d, 0xeb, 0x46, 0x05, 0x36, 0x7e, 0x2f, + 0x9c, 0xa1, 0x86, 0x15, 0xe1, 0xe4, 0xb4, 0x20, + 0x06, 0x44, 0x7b, 0x3c, 0x8b, 0x13, 0x96, 0xf5, + 0x02, 0xb1, 0x4f, 0x3c, 0x2d, 0x4a, 0x16, 0x03, + 0x01, 0x00, 0x86, 0x0f, 0x00, 0x00, 0x82, 0x00, + 0x80, 0x52, 0xb1, 0x0d, 0xfc, 0x85, 0x34, 0x56, + 0xb9, 0xdf, 0xa7, 0x8e, 0xf4, 0xfd, 0x02, 0x46, + 0x8a, 0x23, 0xcc, 0x53, 0x3b, 0x0f, 0xa7, 0x61, + 0xf3, 0xb5, 0xbf, 0xfe, 0x59, 0x77, 0x10, 0xd6, + 0x56, 0x93, 0x19, 0x6b, 0x2c, 0xf1, 0x35, 0x71, + 0xe3, 0x36, 0x2f, 0xa0, 0x90, 0x4e, 0x5a, 0xdf, + 0x8d, 0x06, 0x88, 0xcf, 0xb1, 0x06, 0x56, 0x8b, + 0x74, 0x8f, 0x02, 0x8e, 0x10, 0xd2, 0xab, 0x8d, + 0x3f, 0x3e, 0x02, 0xf1, 0x1a, 0x80, 0x6d, 0x0f, + 0x9e, 0x77, 0xd8, 0xfa, 0x92, 0xb3, 0x16, 0x40, + 0xeb, 0x9e, 0xca, 0xd7, 0xe4, 0x31, 0xcc, 0x63, + 0x5f, 0xe2, 0x4c, 0x85, 0x0e, 0xf2, 0xdd, 0xd3, + 0xfe, 0x7e, 0xa7, 0x60, 0x1c, 0xb4, 0x00, 0xd8, + 0xbe, 0x4b, 0x9b, 0x66, 0x78, 0x0f, 0xfb, 0x3b, + 0x52, 0x30, 0x2b, 0x8b, 0xd9, 0xef, 0x82, 0x0a, + 0xa4, 0x18, 0x1d, 0xb0, 0xb5, 0xbf, 0x54, 0x97, + 0x0c, 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, + 0x03, 0x01, 0x00, 0x30, 0xa1, 0x74, 0x22, 0xd8, + 0x86, 0x6a, 0xbe, 0x53, 0x34, 0x1d, 0xb3, 0x73, + 0xff, 0x51, 0xc0, 0xce, 0x8e, 0x7d, 0x9b, 0xab, + 0xcb, 0x8b, 0x79, 0xae, 0x04, 0x01, 0xa7, 0xf2, + 0x8e, 0x9d, 0xab, 0xa3, 0x73, 0x80, 0x5c, 0xff, + 0x96, 0x20, 0xbb, 0x8d, 0xc0, 0x02, 0x66, 0x6c, + 0x83, 0x4b, 0x78, 0x20, + }, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x30, 0x29, 0xd4, 0xfd, 0x03, 0x8b, + 0x30, 0x20, 0xf7, 0xca, 0xc0, 0x6c, 0x83, 0x5d, + 0x73, 0xcb, 0x81, 0x60, 0xe0, 0x9a, 0x09, 0xcb, + 0x33, 0x03, 0x80, 0x81, 0x4e, 0x84, 0x47, 0xd5, + 0x74, 0x6c, 0x3b, 0xb5, 0xc0, 0x48, 0x0d, 0x52, + 0xdd, 0xbe, 0xc2, 0x06, 0xf5, 0x79, 0x2b, 0x3e, + 0x99, 0x56, 0x94, 0x17, 0x03, 0x01, 0x00, 0x20, + 0x26, 0x46, 0x90, 0x9d, 0xef, 0x59, 0x00, 0xb6, + 0x70, 0xe8, 0x1e, 0x1a, 0x80, 0x8b, 0x04, 0xb2, + 0xfc, 0x51, 0xf8, 0x93, 0xbe, 0x00, 0x28, 0xba, + 0xb8, 0xdc, 0x51, 0x7e, 0x92, 0x80, 0xfa, 0xf2, + 0x17, 0x03, 0x01, 0x00, 0xe0, 0xb8, 0x2e, 0xc4, + 0x6b, 0x3f, 0xda, 0x39, 0x87, 0x7f, 0x03, 0x43, + 0x28, 0xdd, 0xb9, 0xf9, 0x9e, 0x16, 0xf5, 0xce, + 0x3f, 0x7e, 0x6a, 0x7b, 0xb3, 0x60, 0x14, 0xe1, + 0xea, 0x54, 0xc5, 0xe6, 0x05, 0x0a, 0x6c, 0xe0, + 0xef, 0x58, 0x29, 0x8a, 0x77, 0x64, 0x77, 0x5d, + 0x9c, 0xe2, 0xe0, 0x3c, 0x6d, 0x87, 0x82, 0xbe, + 0x47, 0x63, 0xd4, 0xfd, 0x0c, 0x25, 0xc4, 0xb1, + 0xfe, 0x29, 0x6f, 0x84, 0xfb, 0xab, 0x6e, 0xa7, + 0xf9, 0x22, 0x89, 0x97, 0x5b, 0x91, 0x0a, 0x07, + 0xe0, 0xef, 0x3d, 0x67, 0xee, 0x87, 0xa8, 0x33, + 0x02, 0x64, 0x33, 0xca, 0x15, 0x10, 0xb9, 0x57, + 0xd8, 0xe5, 0x1a, 0x4b, 0xe3, 0x45, 0xc1, 0x62, + 0x85, 0x50, 0xf1, 0x79, 0x54, 0xe1, 0x2e, 0x25, + 0x01, 0x3c, 0xdb, 0x2d, 0x39, 0x14, 0x2f, 0x9b, + 0xd0, 0x1d, 0xc1, 0xac, 0x73, 0x7d, 0xa4, 0xed, + 0x89, 0x98, 0xb1, 0xae, 0x8a, 0x9e, 0xc8, 0xa7, + 0xfe, 0x55, 0x27, 0xb5, 0xb5, 0xa2, 0xec, 0x7e, + 0xe3, 0x6b, 0x45, 0x19, 0xfa, 0x20, 0x1c, 0x33, + 0x83, 0x22, 0x33, 0x97, 0xd2, 0x5a, 0xc4, 0xf8, + 0x9a, 0x03, 0x13, 0x85, 0xf2, 0x2b, 0x04, 0x59, + 0x27, 0xd7, 0x0b, 0x42, 0x47, 0x9b, 0x7d, 0x4d, + 0xb2, 0x1a, 0x85, 0x7f, 0x97, 0xc2, 0xf2, 0x10, + 0xf0, 0xfa, 0x4e, 0x4b, 0x62, 0x43, 0x3a, 0x09, + 0x2e, 0xcd, 0x8f, 0xa8, 0xb6, 0x0b, 0x5f, 0x34, + 0xd7, 0x3b, 0xba, 0xd9, 0xe5, 0x01, 0x2d, 0x35, + 0xae, 0xc5, 0x4c, 0xab, 0x40, 0x64, 0xc2, 0xc9, + 0x8c, 0x69, 0x44, 0xf4, 0xb8, 0xb5, 0x3a, 0x05, + 0x3c, 0x29, 0x19, 0xb4, 0x09, 0x17, 0x03, 0x01, + 0x00, 0x20, 0xc8, 0xc5, 0xb7, 0xe3, 0xd2, 0x3e, + 0x27, 0xb5, 0x71, 0x8f, 0x52, 0x0b, 0xce, 0x17, + 0x64, 0x86, 0xa4, 0x34, 0x16, 0x1b, 0x61, 0x64, + 0x7c, 0xb3, 0xf2, 0xe5, 0x3e, 0xfd, 0xdd, 0xfb, + 0x40, 0x78, 0x17, 0x03, 0x01, 0x00, 0x50, 0x8e, + 0x79, 0xf0, 0x8e, 0x76, 0x5d, 0x34, 0x09, 0xdc, + 0xec, 0x6d, 0xc3, 0x43, 0x1d, 0xcb, 0x2d, 0xaa, + 0x08, 0x7a, 0x51, 0x94, 0x4e, 0xc5, 0x26, 0xe4, + 0x0b, 0x8e, 0x8f, 0x51, 0xf2, 0x9f, 0xeb, 0xc3, + 0x18, 0x43, 0x95, 0x15, 0xfc, 0x59, 0x18, 0x25, + 0x47, 0xb6, 0x4a, 0x6e, 0xa3, 0xa4, 0x3b, 0xa3, + 0x47, 0x34, 0x74, 0x6b, 0xc5, 0x3d, 0x41, 0x14, + 0x64, 0xd5, 0x69, 0x5f, 0x77, 0xf3, 0x7c, 0x41, + 0xc6, 0xed, 0x2e, 0xcf, 0xff, 0x40, 0xf2, 0xce, + 0xbb, 0xa7, 0x4e, 0x73, 0x88, 0x98, 0x10, + }, + { + 0x15, 0x03, 0x01, 0x00, 0x20, 0x1a, 0xbc, 0x70, + 0x24, 0xf8, 0xfb, 0xf2, 0x4a, 0xf9, 0x44, 0x1e, + 0x58, 0xf8, 0xaa, 0x41, 0x24, 0xe8, 0x80, 0x33, + 0x45, 0x18, 0xa1, 0x5d, 0xee, 0x16, 0x80, 0xae, + 0x40, 0x41, 0x8e, 0x41, 0x9b, + }, +} + +var tls11ECDHEAESClientScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, + 0x46, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x13, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, + }, + { + 0x16, 0x03, 0x02, 0x00, 0x54, 0x02, 0x00, 0x00, + 0x50, 0x03, 0x02, 0x51, 0x9f, 0xa2, 0x21, 0x1a, + 0xb7, 0x75, 0x42, 0x69, 0xd3, 0x14, 0xdd, 0x05, + 0x1e, 0xda, 0x13, 0x71, 0x8d, 0x6a, 0x45, 0x97, + 0xcb, 0xee, 0x0e, 0x77, 0x01, 0x0d, 0x6e, 0xe5, + 0x22, 0x70, 0x16, 0x20, 0x69, 0xfc, 0xa6, 0x9a, + 0xe8, 0x21, 0xcc, 0x46, 0x65, 0x05, 0xb4, 0x48, + 0x0f, 0x34, 0x63, 0x2c, 0xac, 0xa4, 0xf5, 0x4b, + 0x64, 0xd1, 0x07, 0x13, 0xa7, 0xe4, 0x5b, 0xa3, + 0x4d, 0x31, 0x41, 0x53, 0xc0, 0x13, 0x00, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, + 0x02, 0x16, 0x03, 0x02, 0x02, 0x39, 0x0b, 0x00, + 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, + 0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36, + 0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, + 0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31, + 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, + 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, + 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, + 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, + 0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52, + 0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, + 0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, + 0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, + 0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, + 0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, + 0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, + 0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, + 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, + 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, + 0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, + 0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, + 0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, + 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85, + 0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, + 0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, + 0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, + 0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, + 0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, + 0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, + 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, + 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, + 0x03, 0x02, 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87, + 0x03, 0x00, 0x17, 0x41, 0x04, 0x34, 0xde, 0x50, + 0x32, 0x8f, 0x25, 0x6b, 0x37, 0x2c, 0x36, 0x24, + 0x27, 0x0e, 0xf9, 0x67, 0xb4, 0xf8, 0x29, 0x1c, + 0xa5, 0xa4, 0x59, 0x9a, 0xca, 0x40, 0x26, 0x15, + 0x61, 0x72, 0x34, 0x4a, 0xd3, 0x0c, 0xac, 0x69, + 0xcb, 0x2a, 0x9e, 0xf8, 0x80, 0xfb, 0x7a, 0xc4, + 0xd4, 0x4b, 0x91, 0x1b, 0xbe, 0x24, 0x26, 0xad, + 0x19, 0x24, 0xbe, 0x32, 0x58, 0xfb, 0xc7, 0x77, + 0xce, 0x7e, 0x71, 0x51, 0x1a, 0x00, 0x40, 0x1a, + 0x0b, 0xe8, 0x91, 0x84, 0x64, 0x54, 0xb6, 0x19, + 0xe8, 0xd4, 0x43, 0x7c, 0x09, 0x0c, 0x2e, 0xba, + 0x42, 0xb9, 0x74, 0xc3, 0x6c, 0x06, 0x9b, 0xa6, + 0x7e, 0x92, 0xe9, 0xee, 0x7c, 0x74, 0xa9, 0xd3, + 0x63, 0xf0, 0x16, 0x20, 0x60, 0x71, 0x8e, 0x24, + 0xc7, 0x7f, 0xc5, 0x5b, 0x9c, 0x19, 0x0c, 0x80, + 0x15, 0x61, 0xbf, 0xb6, 0xed, 0x5b, 0x7b, 0x90, + 0xc5, 0x05, 0x13, 0x72, 0x45, 0x79, 0xdf, 0x16, + 0x03, 0x02, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x02, 0x00, 0x46, 0x10, 0x00, 0x00, + 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d, + 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5, + 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd, + 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce, + 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e, + 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56, + 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49, + 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b, + 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x02, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x02, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x50, + 0x32, 0x26, 0x51, 0xbd, 0xbd, 0x3c, 0x4f, 0x72, + 0xbf, 0xbc, 0x91, 0x70, 0x4b, 0x5d, 0x43, 0x4a, + 0x65, 0x26, 0x0d, 0xaa, 0xed, 0x00, 0x91, 0xaf, + 0x4f, 0x47, 0x09, 0xaa, 0x79, 0xc4, 0x47, 0x21, + 0x71, 0xd8, 0x2b, 0xc1, 0x51, 0xc8, 0xef, 0xed, + 0x67, 0xde, 0x97, 0xef, 0x18, 0x53, + }, + { + 0x14, 0x03, 0x02, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x02, 0x00, 0x40, 0x72, 0x20, 0xbf, 0xd1, 0xbd, + 0x83, 0x53, 0x57, 0xb0, 0x4e, 0xac, 0xba, 0x1a, + 0x2b, 0x2d, 0xeb, 0x8a, 0x48, 0x17, 0xfa, 0x69, + 0xf9, 0xb5, 0x94, 0x8e, 0x6f, 0x9c, 0xda, 0x59, + 0xba, 0x6c, 0x7c, 0x82, 0xe2, 0x53, 0xa9, 0x46, + 0xdc, 0x33, 0xa0, 0x9b, 0xf0, 0x1e, 0xf1, 0x53, + 0x83, 0x48, 0xbf, 0x5e, 0xef, 0x03, 0x2b, 0x50, + 0x7a, 0xa6, 0xf8, 0xc3, 0x9e, 0x24, 0x43, 0x3a, + 0xdf, 0x44, 0x3e, + }, + { + 0x17, 0x03, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x0b, 0x8f, + 0x6b, 0xf9, 0xd3, 0x9f, 0x2b, 0x49, 0xe0, 0x62, + 0x9a, 0x0b, 0x3e, 0xa2, 0x72, 0x8b, 0x96, 0x0c, + 0x41, 0x09, 0x95, 0x9e, 0x6b, 0x26, 0xa1, 0x46, + 0xca, 0xb8, 0xb6, 0xd2, 0xd4, 0x15, 0x03, 0x02, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xa0, 0xd4, 0x84, 0xc6, 0x7e, 0x1c, + 0x2f, 0xbd, 0x6b, 0x45, 0x31, 0x1d, 0x7d, 0x8f, + 0x31, 0x39, 0x5a, 0x4e, 0xaa, 0xf1, 0x0a, 0x8a, + 0x6c, 0x33, 0x59, 0x19, 0xd8, 0x75, 0x80, 0xab, + 0x93, 0x81, + }, +} + +var clientChainCertificateScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x02, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x51, 0xa2, 0x9b, 0x8b, 0xd4, + 0xe6, 0x33, 0xa2, 0x70, 0x38, 0x37, 0xba, 0x55, + 0x86, 0xcf, 0x87, 0xea, 0x6d, 0x2c, 0x3e, 0x17, + 0xc2, 0x09, 0xf8, 0x4d, 0xb0, 0x5d, 0x93, 0x2b, + 0x15, 0x99, 0x0c, 0x20, 0x5d, 0x61, 0x21, 0x2c, + 0xed, 0x49, 0x32, 0x29, 0x08, 0x6e, 0x21, 0x58, + 0x00, 0xdb, 0x34, 0xb7, 0x37, 0xcd, 0x27, 0x75, + 0x31, 0x1e, 0x6c, 0x74, 0xa6, 0xef, 0xa2, 0xc4, + 0x2b, 0x6c, 0xc3, 0x03, 0x00, 0x05, 0x00, 0x16, + 0x03, 0x01, 0x03, 0xef, 0x0b, 0x00, 0x03, 0xeb, + 0x00, 0x03, 0xe8, 0x00, 0x03, 0xe5, 0x30, 0x82, + 0x03, 0xe1, 0x30, 0x82, 0x02, 0xc9, 0xa0, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xcc, 0x22, + 0x4c, 0x4b, 0x98, 0xa2, 0x88, 0xfc, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x86, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, + 0x4e, 0x59, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, + 0x6f, 0x6b, 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, + 0x4d, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, + 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e, + 0x6f, 0x72, 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, + 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, + 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x33, 0x30, 0x35, 0x32, 0x36, + 0x32, 0x31, 0x30, 0x35, 0x30, 0x31, 0x5a, 0x17, + 0x0d, 0x32, 0x33, 0x30, 0x35, 0x32, 0x34, 0x32, + 0x31, 0x30, 0x35, 0x30, 0x31, 0x5a, 0x30, 0x81, + 0x86, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, + 0x02, 0x4e, 0x59, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x42, 0x72, + 0x6f, 0x6f, 0x6b, 0x6c, 0x79, 0x6e, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x18, 0x4d, 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x08, 0x6d, 0x79, 0x63, 0x61, + 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x21, 0x30, 0x1f, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, + 0x68, 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, + 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xf0, 0xfb, 0xad, 0x80, 0x5e, 0x37, 0xd3, 0x6d, + 0xee, 0x2e, 0xcc, 0xbc, 0x0c, 0xd7, 0x56, 0x4b, + 0x56, 0x45, 0xcd, 0x28, 0xb6, 0x22, 0xe9, 0xe2, + 0x0f, 0xd1, 0x87, 0x2a, 0x27, 0xce, 0x77, 0x8d, + 0x6e, 0x0e, 0x0f, 0xfb, 0x66, 0xe1, 0xb5, 0x0e, + 0x9a, 0xb6, 0x05, 0x8e, 0xb3, 0xe1, 0xc5, 0x77, + 0x86, 0x5b, 0x46, 0xd2, 0x0b, 0x92, 0x03, 0x1b, + 0x89, 0x0c, 0x1b, 0x10, 0x0e, 0x99, 0x8f, 0xe2, + 0x17, 0xe8, 0xc2, 0x30, 0x00, 0x47, 0xd6, 0xfc, + 0xf9, 0x0f, 0x3b, 0x75, 0x34, 0x8d, 0x4d, 0xb0, + 0x99, 0xb7, 0xa0, 0x6d, 0xa0, 0xb6, 0xad, 0xda, + 0x07, 0x5e, 0x38, 0x2e, 0x02, 0xe4, 0x30, 0x6d, + 0xae, 0x13, 0x72, 0xd4, 0xc8, 0xce, 0x14, 0x07, + 0xae, 0x23, 0x8c, 0x8f, 0x9e, 0x8c, 0x60, 0xd6, + 0x06, 0xb9, 0xef, 0x00, 0x18, 0xc0, 0x1d, 0x25, + 0x1e, 0xda, 0x3e, 0x2f, 0xcf, 0x2b, 0x56, 0x84, + 0x9e, 0x30, 0x21, 0xc7, 0x29, 0xf6, 0x03, 0x8a, + 0x24, 0xf9, 0x34, 0xac, 0x65, 0x9d, 0x80, 0x36, + 0xc8, 0x3b, 0x15, 0x10, 0xbd, 0x51, 0xe9, 0xbc, + 0x02, 0xe1, 0xe9, 0xb3, 0x5a, 0x9a, 0x99, 0x41, + 0x1b, 0x27, 0xa0, 0x4d, 0x50, 0x9e, 0x27, 0x7f, + 0xa1, 0x7d, 0x09, 0x87, 0xbd, 0x8a, 0xca, 0x5f, + 0xb1, 0xa5, 0x08, 0xb8, 0x04, 0xd4, 0x52, 0x89, + 0xaa, 0xe0, 0x7d, 0x42, 0x2e, 0x2f, 0x15, 0xee, + 0x66, 0x57, 0x0f, 0x13, 0x19, 0x45, 0xa8, 0x4b, + 0x5d, 0x81, 0x66, 0xcc, 0x12, 0x37, 0x94, 0x5e, + 0xfd, 0x3c, 0x10, 0x81, 0x51, 0x3f, 0xfa, 0x0f, + 0xdd, 0xa1, 0x89, 0x03, 0xa9, 0x78, 0x91, 0xf5, + 0x3b, 0xf3, 0xbc, 0xac, 0xbe, 0x93, 0x30, 0x2e, + 0xbe, 0xca, 0x7f, 0x46, 0xd3, 0x28, 0xb4, 0x4e, + 0x91, 0x7b, 0x5b, 0x43, 0x6c, 0xaf, 0x9b, 0x5c, + 0x6a, 0x6d, 0x5a, 0xdb, 0x79, 0x5e, 0x6a, 0x6b, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, + 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x6b, 0x1e, 0x00, 0xa8, + 0x9f, 0xfa, 0x7d, 0x00, 0xf9, 0xe0, 0x9d, 0x0f, + 0x90, 0x8c, 0x90, 0xa8, 0xa1, 0x37, 0x6b, 0xda, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x6b, 0x1e, 0x00, + 0xa8, 0x9f, 0xfa, 0x7d, 0x00, 0xf9, 0xe0, 0x9d, + 0x0f, 0x90, 0x8c, 0x90, 0xa8, 0xa1, 0x37, 0x6b, + 0xda, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x01, 0x00, 0xcd, 0x6f, 0x73, 0x4d, 0x56, + 0x0b, 0xf3, 0x2e, 0x1c, 0xe2, 0x02, 0x0c, 0x14, + 0xbb, 0x2f, 0xdd, 0x3c, 0x43, 0xfe, 0xdf, 0x94, + 0x2d, 0xa9, 0x89, 0x81, 0x51, 0xf8, 0x5f, 0xa7, + 0xa0, 0x13, 0xaa, 0xcc, 0xb0, 0x18, 0xe2, 0x57, + 0x3e, 0x0d, 0x29, 0x93, 0xe8, 0x95, 0xd5, 0x1b, + 0x53, 0xd2, 0x51, 0xf2, 0xbd, 0xf5, 0x9e, 0x7b, + 0x22, 0x65, 0x62, 0x5c, 0xc4, 0x4c, 0x1d, 0xe8, + 0xe9, 0xc3, 0xd4, 0x2b, 0xe7, 0x78, 0xcb, 0x10, + 0xf3, 0xfe, 0x06, 0x83, 0xdc, 0x3a, 0x1e, 0x62, + 0x10, 0xc0, 0x46, 0x77, 0xc6, 0x9d, 0x9f, 0xab, + 0x96, 0x25, 0x5c, 0xfb, 0x26, 0xc1, 0x15, 0x1f, + 0xa5, 0x33, 0xee, 0x4f, 0x9a, 0x14, 0x6a, 0x14, + 0x97, 0x93, 0x2b, 0x95, 0x0b, 0xdc, 0xa8, 0xd7, + 0x69, 0x2e, 0xf0, 0x01, 0x0e, 0xfd, 0x4e, 0xd0, + 0xd9, 0xa8, 0xe5, 0x65, 0xde, 0xfb, 0xca, 0xca, + 0x1c, 0x5f, 0xf9, 0x53, 0xa0, 0x87, 0xe7, 0x33, + 0x9b, 0x2f, 0xcf, 0xe4, 0x13, 0xfc, 0xec, 0x7a, + 0x6c, 0xb0, 0x90, 0x13, 0x9b, 0xb6, 0xc5, 0x03, + 0xf6, 0x0e, 0x5e, 0xe2, 0xe4, 0x26, 0xc1, 0x7e, + 0x53, 0xfe, 0x69, 0xa3, 0xc7, 0xd8, 0x8e, 0x6e, + 0x94, 0x32, 0xa0, 0xde, 0xca, 0xb6, 0xcc, 0xd6, + 0x01, 0xd5, 0x78, 0x40, 0x28, 0x63, 0x9b, 0xee, + 0xcf, 0x09, 0x3b, 0x35, 0x04, 0xf0, 0x14, 0x02, + 0xf6, 0x80, 0x0e, 0x90, 0xb2, 0x94, 0xd2, 0x25, + 0x16, 0xb8, 0x7a, 0x76, 0x87, 0x84, 0x9f, 0x84, + 0xc5, 0xaf, 0xc2, 0x6d, 0x68, 0x7a, 0x84, 0x9c, + 0xc6, 0x8a, 0x63, 0x60, 0x87, 0x6a, 0x25, 0xc1, + 0xa1, 0x78, 0x0f, 0xba, 0xe8, 0x5f, 0xe1, 0xba, + 0xac, 0xa4, 0x6f, 0xdd, 0x09, 0x3f, 0x12, 0xcb, + 0x1d, 0xf3, 0xcf, 0x48, 0xd7, 0xd3, 0x26, 0xe8, + 0x9c, 0xc3, 0x53, 0xb3, 0xba, 0xdc, 0x32, 0x99, + 0x98, 0x96, 0xd6, 0x16, 0x03, 0x01, 0x00, 0x99, + 0x0d, 0x00, 0x00, 0x91, 0x03, 0x01, 0x02, 0x40, + 0x00, 0x8b, 0x00, 0x89, 0x30, 0x81, 0x86, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, + 0x59, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, + 0x6b, 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, + 0x79, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, + 0x72, 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, + 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, + 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, + 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x0e, 0x00, 0x00, + 0x00, + }, + { + 0x16, 0x03, 0x01, 0x0a, 0xfb, 0x0b, 0x00, 0x0a, + 0xf7, 0x00, 0x0a, 0xf4, 0x00, 0x03, 0x7e, 0x30, + 0x82, 0x03, 0x7a, 0x30, 0x82, 0x02, 0x62, 0x02, + 0x09, 0x00, 0xb4, 0x47, 0x58, 0x57, 0x2b, 0x67, + 0xc8, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0x80, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79, + 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79, 0x20, 0x43, + 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63, 0x61, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, + 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, + 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, + 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x34, + 0x34, 0x30, 0x30, 0x5a, 0x30, 0x7d, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x08, 0x4e, 0x65, + 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79, + 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x07, 0x4d, 0x79, 0x20, 0x4c, + 0x65, 0x61, 0x66, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x6d, 0x79, + 0x6c, 0x65, 0x61, 0x66, 0x2e, 0x63, 0x6f, 0x6d, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69, + 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa0, 0xa3, 0xef, 0xc1, + 0x44, 0x7d, 0xa2, 0xe3, 0x71, 0x98, 0x27, 0x63, + 0xb3, 0x1d, 0x71, 0x50, 0xa6, 0x34, 0x15, 0xcb, + 0xc9, 0x2a, 0xc3, 0xea, 0xe4, 0x9e, 0x9c, 0x49, + 0xa6, 0x01, 0x9b, 0x7e, 0xa9, 0xb5, 0x7a, 0xff, + 0x15, 0x92, 0x71, 0xc8, 0x97, 0x9c, 0x25, 0xb7, + 0x79, 0x2b, 0xff, 0xab, 0xc6, 0xb1, 0xa7, 0x00, + 0x90, 0xb2, 0x8b, 0xd7, 0x71, 0xd5, 0xc2, 0x3a, + 0xe6, 0x82, 0x42, 0x37, 0x89, 0x41, 0x04, 0xb0, + 0xba, 0xc7, 0x5b, 0x8a, 0x43, 0x9f, 0x97, 0x39, + 0x0c, 0x0f, 0xd5, 0x6d, 0x9e, 0x8d, 0xeb, 0xc0, + 0x26, 0xc5, 0x18, 0xe8, 0x7a, 0x3d, 0x32, 0x2e, + 0x38, 0x90, 0x40, 0x5b, 0x39, 0x2c, 0x07, 0xcb, + 0x24, 0x10, 0xc5, 0xc9, 0x3b, 0xe3, 0x66, 0x47, + 0x57, 0xb9, 0x6a, 0xad, 0x44, 0xf8, 0xd0, 0x70, + 0x62, 0x3b, 0x8e, 0xed, 0x60, 0x5f, 0x22, 0xf8, + 0xb8, 0x0c, 0xc9, 0x41, 0x2b, 0xc9, 0x80, 0x6e, + 0x4e, 0x1b, 0xe1, 0x20, 0xfc, 0x47, 0xa4, 0xac, + 0xc3, 0x3f, 0xe6, 0xc2, 0x81, 0x79, 0x03, 0x37, + 0x25, 0x89, 0xca, 0xd6, 0xa5, 0x46, 0x91, 0x63, + 0x41, 0xc5, 0x3e, 0xd5, 0xed, 0x7f, 0x4f, 0x8d, + 0x06, 0xc0, 0x89, 0x00, 0xbe, 0x37, 0x7b, 0x7e, + 0x73, 0xca, 0x70, 0x00, 0x14, 0x34, 0xbe, 0x47, + 0xbc, 0xb2, 0x6a, 0x28, 0xa5, 0x29, 0x84, 0xa8, + 0x9d, 0xc8, 0x1e, 0x77, 0x66, 0x1f, 0x9f, 0xaa, + 0x2b, 0x47, 0xdb, 0xdd, 0x6b, 0x9c, 0xa8, 0xfc, + 0x82, 0x36, 0x94, 0x62, 0x0d, 0x5c, 0x3f, 0xb2, + 0x01, 0xb4, 0xa5, 0xb8, 0xc6, 0x0e, 0x94, 0x5b, + 0xec, 0x5e, 0xbb, 0x7a, 0x63, 0x24, 0xf1, 0xf9, + 0xd6, 0x50, 0x08, 0xc1, 0xa3, 0xcc, 0x90, 0x07, + 0x5b, 0x04, 0x04, 0x42, 0x74, 0xcf, 0x37, 0xfa, + 0xf0, 0xa5, 0xd9, 0xd3, 0x86, 0x89, 0x89, 0x18, + 0xf3, 0x4c, 0xe2, 0x11, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x90, 0xbb, 0xf9, + 0x5e, 0xba, 0x17, 0x1f, 0xac, 0x21, 0x9f, 0x6b, + 0x4a, 0x46, 0xd0, 0x6d, 0x3c, 0x8f, 0x3d, 0xf8, + 0x5e, 0x3e, 0x72, 0xaf, 0xa0, 0x1a, 0xf3, 0xff, + 0x89, 0xac, 0x5b, 0x7a, 0xe2, 0x91, 0x2a, 0x23, + 0x85, 0xc6, 0x4d, 0x47, 0x67, 0x01, 0x08, 0xa8, + 0x05, 0x1d, 0x01, 0x60, 0x50, 0x5f, 0x59, 0xad, + 0xfe, 0x7b, 0xc6, 0x0c, 0x54, 0x90, 0x68, 0x70, + 0x67, 0x2e, 0xed, 0x87, 0xf8, 0x69, 0x8a, 0xac, + 0x32, 0xfe, 0x6f, 0x90, 0x19, 0x2a, 0x64, 0x8d, + 0x82, 0x66, 0x05, 0x43, 0x88, 0xee, 0xf2, 0x30, + 0xed, 0xa4, 0x8f, 0xbf, 0xd6, 0x57, 0x20, 0xd4, + 0x43, 0x1d, 0x52, 0x96, 0x6f, 0xae, 0x09, 0x96, + 0x01, 0x52, 0x38, 0xe3, 0xaf, 0x99, 0xd7, 0xdc, + 0x14, 0x99, 0xc4, 0x8b, 0x0e, 0x04, 0x0f, 0xb3, + 0x14, 0x14, 0xd4, 0xa5, 0x93, 0xe1, 0xc9, 0x8a, + 0x81, 0xef, 0x63, 0xfc, 0x36, 0x77, 0x05, 0x06, + 0xf0, 0x2a, 0x04, 0x0a, 0xbe, 0x2e, 0xce, 0x81, + 0x3d, 0x23, 0xa1, 0xda, 0xd8, 0xeb, 0xc6, 0xea, + 0x5e, 0xcf, 0x28, 0x36, 0x51, 0x31, 0x95, 0x5e, + 0x40, 0x04, 0xed, 0xac, 0xc1, 0xc8, 0x56, 0x69, + 0x87, 0xec, 0x3b, 0x03, 0x3e, 0x9d, 0x0f, 0x4c, + 0x4c, 0xeb, 0xd7, 0xba, 0x26, 0xdf, 0xe3, 0xde, + 0x10, 0xee, 0x93, 0x62, 0x8d, 0x73, 0x52, 0x6e, + 0xff, 0x37, 0x36, 0x98, 0x7b, 0x2d, 0x56, 0x4c, + 0xba, 0x09, 0xb8, 0xa7, 0xf0, 0x3b, 0x16, 0x81, + 0xca, 0xdb, 0x43, 0xab, 0xec, 0x4c, 0x6e, 0x7c, + 0xc1, 0x0b, 0x22, 0x22, 0x43, 0x1d, 0xb6, 0x0c, + 0xc1, 0xb9, 0xcf, 0xe4, 0x53, 0xee, 0x1d, 0x3e, + 0x88, 0xa7, 0x13, 0xbe, 0x7f, 0xbd, 0xae, 0x72, + 0xcf, 0xcd, 0x63, 0xd2, 0xc3, 0x18, 0x58, 0x92, + 0xa2, 0xad, 0xb5, 0x09, 0x9d, 0x91, 0x03, 0xdd, + 0x3c, 0xe2, 0x1c, 0xde, 0x78, 0x00, 0x03, 0x88, + 0x30, 0x82, 0x03, 0x84, 0x30, 0x82, 0x02, 0x6c, + 0x02, 0x09, 0x00, 0xab, 0xed, 0xa6, 0xe4, 0x4a, + 0x2b, 0x2b, 0xf8, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, + 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, + 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69, + 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x31, + 0x38, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x33, + 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x31, 0x38, + 0x34, 0x30, 0x5a, 0x30, 0x81, 0x80, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, + 0x6c, 0x79, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79, + 0x20, 0x43, 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63, + 0x61, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, + 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, + 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, + 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, + 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce, + 0x13, 0xf0, 0x72, 0xb0, 0x61, 0xc8, 0x18, 0x37, + 0x8a, 0x41, 0x3d, 0x20, 0xa1, 0x1c, 0xcb, 0xbf, + 0xf6, 0x3b, 0x74, 0x26, 0x2a, 0x96, 0x11, 0xec, + 0x53, 0xa1, 0xcc, 0x7d, 0x77, 0x56, 0x45, 0x0f, + 0x36, 0xb7, 0xf2, 0x48, 0x92, 0x1a, 0x62, 0xcc, + 0xb6, 0xc0, 0xa1, 0x2f, 0x44, 0x2b, 0xc1, 0x89, + 0xcb, 0x6e, 0x1e, 0xdb, 0x57, 0x92, 0xd5, 0x97, + 0x60, 0x8c, 0x41, 0x2c, 0xd9, 0x20, 0xfe, 0xe9, + 0x1f, 0x8e, 0xfc, 0x7f, 0x02, 0x44, 0x0f, 0x28, + 0x81, 0xd6, 0x0c, 0xcd, 0xbc, 0xf0, 0x57, 0x6c, + 0xcc, 0xa7, 0xba, 0x06, 0xa0, 0xa6, 0x91, 0xda, + 0xef, 0x46, 0x8a, 0x60, 0x0f, 0x52, 0x6c, 0x90, + 0x6c, 0x8c, 0x44, 0xaf, 0xb0, 0x9d, 0x90, 0xba, + 0x21, 0x58, 0xa0, 0x3c, 0xee, 0x54, 0xb5, 0x29, + 0x26, 0x1f, 0x0a, 0xac, 0xef, 0x48, 0x68, 0x33, + 0xd0, 0x33, 0xd0, 0x8b, 0x1a, 0xec, 0x6e, 0x2f, + 0xb5, 0x4a, 0x53, 0xc2, 0x1a, 0xd2, 0xf1, 0x50, + 0x05, 0x59, 0x5c, 0xd9, 0xda, 0x03, 0x0a, 0x47, + 0xb7, 0xdd, 0xf7, 0x3a, 0x69, 0xf5, 0x4e, 0xea, + 0x4a, 0xc2, 0xca, 0x54, 0xb0, 0x8b, 0x76, 0xe1, + 0x02, 0x2d, 0x52, 0x67, 0xb9, 0xdd, 0x50, 0xc9, + 0x3b, 0x07, 0x24, 0x22, 0x6a, 0x00, 0x1d, 0x58, + 0x83, 0xa8, 0xec, 0x95, 0xf1, 0xda, 0xe2, 0x73, + 0xa0, 0xa1, 0x72, 0x60, 0x9e, 0x86, 0x53, 0xcb, + 0x45, 0xa8, 0xc2, 0xa0, 0x50, 0xa0, 0x53, 0xd6, + 0xfc, 0x18, 0x84, 0xb5, 0x4a, 0x26, 0xd0, 0xa2, + 0xaa, 0xd0, 0xff, 0xb6, 0xfe, 0x3a, 0x9c, 0xb5, + 0x19, 0x3b, 0x3f, 0xe1, 0x48, 0x0d, 0xa4, 0x09, + 0x4f, 0x83, 0xc9, 0xc0, 0xc9, 0xa6, 0x0b, 0x58, + 0x1f, 0x1c, 0x7b, 0xac, 0xa2, 0x42, 0xbc, 0x61, + 0xf4, 0x21, 0x8a, 0x00, 0xda, 0x14, 0xa0, 0x60, + 0x03, 0xfe, 0x93, 0x12, 0x6c, 0x56, 0xcd, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x25, 0x29, 0x3b, 0x1e, 0xc3, 0x58, 0x32, 0xe6, + 0x23, 0xc8, 0xee, 0x18, 0xf0, 0x1d, 0x62, 0x6d, + 0x3b, 0x59, 0x99, 0x3a, 0xfe, 0x49, 0x72, 0x07, + 0x3f, 0x58, 0x93, 0xdb, 0xc0, 0xaf, 0xb0, 0xb3, + 0x5c, 0xd1, 0x5c, 0x98, 0xc8, 0xea, 0x4a, 0xe4, + 0x58, 0x73, 0x0d, 0x57, 0xc5, 0x13, 0x7c, 0x5c, + 0x79, 0x66, 0xda, 0x04, 0x1d, 0xe5, 0x98, 0xda, + 0x35, 0x47, 0x44, 0xb0, 0xd2, 0x7a, 0x66, 0x9d, + 0xcd, 0x41, 0xa5, 0x8f, 0xa1, 0x11, 0xb2, 0x1a, + 0x87, 0xc0, 0xcd, 0x55, 0xed, 0xb4, 0x7b, 0x33, + 0x72, 0xeb, 0xf7, 0xe3, 0x7b, 0x8b, 0x02, 0x86, + 0xe9, 0x2b, 0x26, 0x32, 0x9f, 0x99, 0xf1, 0xcb, + 0x93, 0xab, 0xb9, 0x16, 0xb3, 0x9a, 0xb2, 0x22, + 0x13, 0x21, 0x1f, 0x5b, 0xcc, 0xa2, 0x59, 0xbb, + 0x69, 0xf2, 0xb8, 0x07, 0x80, 0xce, 0x0c, 0xf7, + 0x98, 0x4c, 0x85, 0xc2, 0x96, 0x6a, 0x22, 0x05, + 0xe9, 0xbe, 0x48, 0xb0, 0x02, 0x5b, 0x69, 0x28, + 0x18, 0x88, 0x96, 0xe3, 0xd7, 0xc6, 0x7a, 0xd3, + 0xe9, 0x99, 0xff, 0x9d, 0xc3, 0x61, 0x4d, 0x9a, + 0x96, 0xf2, 0xc6, 0x33, 0x4d, 0xe5, 0x5d, 0x5a, + 0x68, 0x64, 0x5a, 0x82, 0x35, 0x65, 0x25, 0xe3, + 0x8c, 0x5b, 0xb0, 0xf6, 0x96, 0x56, 0xbc, 0xbf, + 0x97, 0x76, 0x4b, 0x66, 0x44, 0x81, 0xa4, 0xc4, + 0xa7, 0x31, 0xc5, 0xa1, 0x4f, 0xe8, 0xa4, 0xca, + 0x20, 0xf5, 0x01, 0x5b, 0x99, 0x4f, 0x5a, 0xf4, + 0xf0, 0x78, 0xbf, 0x71, 0x49, 0xd5, 0xf1, 0xc1, + 0xa2, 0x18, 0xfd, 0x72, 0x5b, 0x16, 0xe8, 0x92, + 0xc7, 0x37, 0x48, 0xaf, 0xee, 0x24, 0xfc, 0x35, + 0x0b, 0xc2, 0xdd, 0x05, 0xc7, 0x6e, 0xa3, 0x29, + 0xbb, 0x29, 0x7d, 0xd3, 0x2b, 0x94, 0x80, 0xc3, + 0x40, 0x53, 0x0e, 0x03, 0x54, 0x3d, 0x7b, 0x8b, + 0xce, 0xf9, 0xa4, 0x03, 0x27, 0x63, 0xec, 0x51, + 0x00, 0x03, 0xe5, 0x30, 0x82, 0x03, 0xe1, 0x30, + 0x82, 0x02, 0xc9, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x09, 0x00, 0xcc, 0x22, 0x4c, 0x4b, 0x98, + 0xa2, 0x88, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, + 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, + 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69, + 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x30, + 0x35, 0x30, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x33, + 0x30, 0x35, 0x32, 0x34, 0x32, 0x31, 0x30, 0x35, + 0x30, 0x31, 0x5a, 0x30, 0x81, 0x86, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, + 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, + 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, + 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, + 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xf0, 0xfb, 0xad, + 0x80, 0x5e, 0x37, 0xd3, 0x6d, 0xee, 0x2e, 0xcc, + 0xbc, 0x0c, 0xd7, 0x56, 0x4b, 0x56, 0x45, 0xcd, + 0x28, 0xb6, 0x22, 0xe9, 0xe2, 0x0f, 0xd1, 0x87, + 0x2a, 0x27, 0xce, 0x77, 0x8d, 0x6e, 0x0e, 0x0f, + 0xfb, 0x66, 0xe1, 0xb5, 0x0e, 0x9a, 0xb6, 0x05, + 0x8e, 0xb3, 0xe1, 0xc5, 0x77, 0x86, 0x5b, 0x46, + 0xd2, 0x0b, 0x92, 0x03, 0x1b, 0x89, 0x0c, 0x1b, + 0x10, 0x0e, 0x99, 0x8f, 0xe2, 0x17, 0xe8, 0xc2, + 0x30, 0x00, 0x47, 0xd6, 0xfc, 0xf9, 0x0f, 0x3b, + 0x75, 0x34, 0x8d, 0x4d, 0xb0, 0x99, 0xb7, 0xa0, + 0x6d, 0xa0, 0xb6, 0xad, 0xda, 0x07, 0x5e, 0x38, + 0x2e, 0x02, 0xe4, 0x30, 0x6d, 0xae, 0x13, 0x72, + 0xd4, 0xc8, 0xce, 0x14, 0x07, 0xae, 0x23, 0x8c, + 0x8f, 0x9e, 0x8c, 0x60, 0xd6, 0x06, 0xb9, 0xef, + 0x00, 0x18, 0xc0, 0x1d, 0x25, 0x1e, 0xda, 0x3e, + 0x2f, 0xcf, 0x2b, 0x56, 0x84, 0x9e, 0x30, 0x21, + 0xc7, 0x29, 0xf6, 0x03, 0x8a, 0x24, 0xf9, 0x34, + 0xac, 0x65, 0x9d, 0x80, 0x36, 0xc8, 0x3b, 0x15, + 0x10, 0xbd, 0x51, 0xe9, 0xbc, 0x02, 0xe1, 0xe9, + 0xb3, 0x5a, 0x9a, 0x99, 0x41, 0x1b, 0x27, 0xa0, + 0x4d, 0x50, 0x9e, 0x27, 0x7f, 0xa1, 0x7d, 0x09, + 0x87, 0xbd, 0x8a, 0xca, 0x5f, 0xb1, 0xa5, 0x08, + 0xb8, 0x04, 0xd4, 0x52, 0x89, 0xaa, 0xe0, 0x7d, + 0x42, 0x2e, 0x2f, 0x15, 0xee, 0x66, 0x57, 0x0f, + 0x13, 0x19, 0x45, 0xa8, 0x4b, 0x5d, 0x81, 0x66, + 0xcc, 0x12, 0x37, 0x94, 0x5e, 0xfd, 0x3c, 0x10, + 0x81, 0x51, 0x3f, 0xfa, 0x0f, 0xdd, 0xa1, 0x89, + 0x03, 0xa9, 0x78, 0x91, 0xf5, 0x3b, 0xf3, 0xbc, + 0xac, 0xbe, 0x93, 0x30, 0x2e, 0xbe, 0xca, 0x7f, + 0x46, 0xd3, 0x28, 0xb4, 0x4e, 0x91, 0x7b, 0x5b, + 0x43, 0x6c, 0xaf, 0x9b, 0x5c, 0x6a, 0x6d, 0x5a, + 0xdb, 0x79, 0x5e, 0x6a, 0x6b, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa, 0x7d, + 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c, 0x90, + 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa, + 0x7d, 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c, + 0x90, 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x0c, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, + 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0xcd, 0x6f, 0x73, 0x4d, 0x56, 0x0b, 0xf3, 0x2e, + 0x1c, 0xe2, 0x02, 0x0c, 0x14, 0xbb, 0x2f, 0xdd, + 0x3c, 0x43, 0xfe, 0xdf, 0x94, 0x2d, 0xa9, 0x89, + 0x81, 0x51, 0xf8, 0x5f, 0xa7, 0xa0, 0x13, 0xaa, + 0xcc, 0xb0, 0x18, 0xe2, 0x57, 0x3e, 0x0d, 0x29, + 0x93, 0xe8, 0x95, 0xd5, 0x1b, 0x53, 0xd2, 0x51, + 0xf2, 0xbd, 0xf5, 0x9e, 0x7b, 0x22, 0x65, 0x62, + 0x5c, 0xc4, 0x4c, 0x1d, 0xe8, 0xe9, 0xc3, 0xd4, + 0x2b, 0xe7, 0x78, 0xcb, 0x10, 0xf3, 0xfe, 0x06, + 0x83, 0xdc, 0x3a, 0x1e, 0x62, 0x10, 0xc0, 0x46, + 0x77, 0xc6, 0x9d, 0x9f, 0xab, 0x96, 0x25, 0x5c, + 0xfb, 0x26, 0xc1, 0x15, 0x1f, 0xa5, 0x33, 0xee, + 0x4f, 0x9a, 0x14, 0x6a, 0x14, 0x97, 0x93, 0x2b, + 0x95, 0x0b, 0xdc, 0xa8, 0xd7, 0x69, 0x2e, 0xf0, + 0x01, 0x0e, 0xfd, 0x4e, 0xd0, 0xd9, 0xa8, 0xe5, + 0x65, 0xde, 0xfb, 0xca, 0xca, 0x1c, 0x5f, 0xf9, + 0x53, 0xa0, 0x87, 0xe7, 0x33, 0x9b, 0x2f, 0xcf, + 0xe4, 0x13, 0xfc, 0xec, 0x7a, 0x6c, 0xb0, 0x90, + 0x13, 0x9b, 0xb6, 0xc5, 0x03, 0xf6, 0x0e, 0x5e, + 0xe2, 0xe4, 0x26, 0xc1, 0x7e, 0x53, 0xfe, 0x69, + 0xa3, 0xc7, 0xd8, 0x8e, 0x6e, 0x94, 0x32, 0xa0, + 0xde, 0xca, 0xb6, 0xcc, 0xd6, 0x01, 0xd5, 0x78, + 0x40, 0x28, 0x63, 0x9b, 0xee, 0xcf, 0x09, 0x3b, + 0x35, 0x04, 0xf0, 0x14, 0x02, 0xf6, 0x80, 0x0e, + 0x90, 0xb2, 0x94, 0xd2, 0x25, 0x16, 0xb8, 0x7a, + 0x76, 0x87, 0x84, 0x9f, 0x84, 0xc5, 0xaf, 0xc2, + 0x6d, 0x68, 0x7a, 0x84, 0x9c, 0xc6, 0x8a, 0x63, + 0x60, 0x87, 0x6a, 0x25, 0xc1, 0xa1, 0x78, 0x0f, + 0xba, 0xe8, 0x5f, 0xe1, 0xba, 0xac, 0xa4, 0x6f, + 0xdd, 0x09, 0x3f, 0x12, 0xcb, 0x1d, 0xf3, 0xcf, + 0x48, 0xd7, 0xd3, 0x26, 0xe8, 0x9c, 0xc3, 0x53, + 0xb3, 0xba, 0xdc, 0x32, 0x99, 0x98, 0x96, 0xd6, + 0x16, 0x03, 0x01, 0x01, 0x06, 0x10, 0x00, 0x01, + 0x02, 0x01, 0x00, 0x6e, 0xea, 0x15, 0x6f, 0x21, + 0xbd, 0x2d, 0x14, 0xde, 0x9d, 0x02, 0xeb, 0xdf, + 0x3b, 0x09, 0x75, 0xaf, 0x32, 0x80, 0x0c, 0xe2, + 0xc2, 0x7b, 0x0d, 0xca, 0x24, 0x96, 0xf6, 0x3e, + 0xa5, 0x97, 0xba, 0x0c, 0x50, 0x7e, 0xb3, 0x68, + 0x58, 0xc6, 0xd8, 0xec, 0xab, 0xa9, 0xd9, 0x3a, + 0xb1, 0x49, 0xea, 0x2f, 0xd7, 0xdb, 0x15, 0x1b, + 0xb5, 0xaf, 0xec, 0xcc, 0x40, 0x5c, 0xe6, 0x0f, + 0xc4, 0x33, 0x71, 0xe7, 0x41, 0xc0, 0x04, 0x89, + 0x60, 0x3e, 0xb7, 0xe6, 0xda, 0x38, 0x62, 0x27, + 0x6a, 0xd9, 0xfb, 0x93, 0x94, 0x9d, 0xc1, 0x63, + 0x92, 0x5c, 0x88, 0x19, 0x38, 0x81, 0x79, 0x9d, + 0x59, 0x48, 0x5e, 0xd3, 0xc8, 0xea, 0xcb, 0x6e, + 0x66, 0x66, 0x03, 0xdc, 0x0c, 0x2d, 0x95, 0xb1, + 0x4d, 0x68, 0xc7, 0xc5, 0x6e, 0xfa, 0x94, 0x14, + 0xdf, 0x2c, 0x70, 0x69, 0x04, 0xf4, 0x69, 0xf1, + 0xf0, 0x07, 0xbd, 0x23, 0x53, 0x63, 0xb3, 0x41, + 0xec, 0xa7, 0x10, 0xa5, 0x04, 0x84, 0x24, 0xb5, + 0xf5, 0x0c, 0x0f, 0x5d, 0x02, 0x47, 0x79, 0x60, + 0x76, 0xbb, 0xdf, 0x60, 0xa6, 0xd7, 0x4d, 0x08, + 0x7d, 0xa6, 0x85, 0x4f, 0x61, 0xac, 0x96, 0x3d, + 0xbc, 0xaf, 0x07, 0xb0, 0x7c, 0xb6, 0x23, 0x3e, + 0x1f, 0x0a, 0x62, 0x77, 0x97, 0x77, 0xae, 0x33, + 0x55, 0x0f, 0x85, 0xdf, 0xdc, 0xbe, 0xc6, 0xe0, + 0xe0, 0x14, 0x83, 0x4c, 0x50, 0xf0, 0xe5, 0x2d, + 0xdc, 0x0b, 0x74, 0x7f, 0xc3, 0x28, 0x98, 0x16, + 0xda, 0x74, 0xe6, 0x40, 0xc2, 0xf0, 0xea, 0xc0, + 0x00, 0xd5, 0xfc, 0x16, 0xe4, 0x43, 0xa1, 0xfc, + 0x31, 0x19, 0x81, 0x62, 0xec, 0x2b, 0xfe, 0xcc, + 0xe8, 0x19, 0xed, 0xa1, 0x1e, 0x6a, 0x49, 0x73, + 0xde, 0xc4, 0xe9, 0x22, 0x0a, 0x21, 0xde, 0x45, + 0x1e, 0x55, 0x12, 0xd9, 0x44, 0xef, 0x4e, 0xaa, + 0x5e, 0x26, 0x57, 0x16, 0x03, 0x01, 0x01, 0x06, + 0x0f, 0x00, 0x01, 0x02, 0x01, 0x00, 0x23, 0xde, + 0xb0, 0x39, 0x60, 0xe9, 0x82, 0xb8, 0xed, 0x17, + 0x78, 0xd2, 0x37, 0x0e, 0x85, 0x69, 0xda, 0xcc, + 0x9f, 0x54, 0x4d, 0xda, 0xce, 0xe8, 0x5a, 0xeb, + 0x3c, 0x61, 0x4c, 0x7a, 0x84, 0x1f, 0x21, 0x03, + 0xb3, 0x8a, 0x74, 0x3b, 0x6a, 0x9e, 0x4f, 0x44, + 0xd9, 0x75, 0x0a, 0xd8, 0x7e, 0x56, 0xa3, 0xef, + 0x5a, 0xfe, 0x8a, 0x35, 0xce, 0x29, 0x18, 0xfe, + 0xa6, 0x61, 0x8e, 0x8f, 0x00, 0x90, 0x2d, 0x85, + 0xe3, 0x6c, 0x0e, 0x8d, 0x8c, 0x27, 0x80, 0x8c, + 0x9f, 0x51, 0xe9, 0xd3, 0xe6, 0x7d, 0x70, 0xe9, + 0xfb, 0xcb, 0xb8, 0x24, 0x94, 0x30, 0x9b, 0xba, + 0x01, 0x14, 0x49, 0x9f, 0xaf, 0x09, 0xd8, 0x26, + 0x1b, 0x23, 0xa4, 0xb8, 0xd9, 0x44, 0x0a, 0xdc, + 0x4e, 0x27, 0xe7, 0x32, 0xf5, 0x9c, 0xf3, 0x8d, + 0xa0, 0xc5, 0xc4, 0xbe, 0x92, 0x02, 0x85, 0x4f, + 0x33, 0x8f, 0xa7, 0xf7, 0x87, 0xa9, 0x44, 0xf3, + 0x64, 0xbd, 0x32, 0x04, 0xeb, 0xc5, 0xc3, 0x62, + 0xe9, 0xda, 0x2f, 0x95, 0x5c, 0xf7, 0x58, 0x3e, + 0xad, 0x35, 0xd7, 0x7e, 0xad, 0xdd, 0x32, 0x8d, + 0xce, 0x81, 0x08, 0xad, 0x49, 0xf7, 0xdb, 0xf7, + 0xaf, 0xe3, 0xc6, 0xb2, 0xdd, 0x76, 0x0c, 0xcf, + 0x0f, 0x87, 0x79, 0x90, 0x10, 0x79, 0xc6, 0xc8, + 0x7b, 0xe6, 0x23, 0xf2, 0xda, 0x33, 0xca, 0xe1, + 0xf0, 0x59, 0x42, 0x43, 0x03, 0x56, 0x19, 0xe3, + 0x8b, 0xe6, 0xa8, 0x70, 0xbc, 0x80, 0xfa, 0x24, + 0xae, 0x03, 0x13, 0x30, 0x0d, 0x1f, 0xab, 0xb7, + 0x82, 0xd9, 0x24, 0x90, 0x80, 0xbf, 0x75, 0xe1, + 0x0d, 0x1c, 0xb2, 0xfe, 0x92, 0x2c, 0x4d, 0x21, + 0xe9, 0x5d, 0xa1, 0x68, 0xf3, 0x16, 0xd8, 0x3f, + 0xb2, 0xc3, 0x00, 0x3e, 0xd8, 0x42, 0x25, 0x5c, + 0x90, 0x11, 0xc0, 0x1b, 0xd4, 0x26, 0x5c, 0x37, + 0x47, 0xbd, 0xf8, 0x1e, 0x34, 0xa9, 0x14, 0x03, + 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, + 0x24, 0x8f, 0x94, 0x7e, 0x01, 0xee, 0xd5, 0x4f, + 0x83, 0x41, 0x31, 0xc0, 0x36, 0x81, 0x46, 0xc3, + 0xc0, 0xcc, 0x9c, 0xea, 0x0f, 0x29, 0x04, 0x10, + 0x43, 0x1e, 0x08, 0x6e, 0x08, 0xce, 0xb2, 0x62, + 0xa6, 0x0f, 0x68, 0x9f, 0x99, + }, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x24, 0xd9, 0x46, 0x5b, 0xbf, 0xfd, + 0x8a, 0xa1, 0x08, 0xd5, 0xf3, 0x0c, 0x1c, 0xd8, + 0xa8, 0xb3, 0xe5, 0x89, 0x83, 0x9e, 0x23, 0x47, + 0x81, 0x66, 0x77, 0x11, 0x98, 0xe5, 0xf4, 0xac, + 0x06, 0xe9, 0x4c, 0x05, 0x8b, 0xc4, 0x16, + }, + { + 0x17, 0x03, 0x01, 0x00, 0x1a, 0xc5, 0x28, 0xfd, + 0x71, 0xc0, 0xe6, 0x89, 0xb8, 0x82, 0x92, 0x1b, + 0xdd, 0x39, 0xe5, 0xbf, 0x41, 0x82, 0x1f, 0xc1, + 0xbc, 0x85, 0xe5, 0x32, 0x1b, 0x93, 0x46, 0x15, + 0x03, 0x01, 0x00, 0x16, 0x1a, 0x8b, 0x10, 0x42, + 0x12, 0xb2, 0xbd, 0xd3, 0xf1, 0x74, 0x1f, 0xc2, + 0x10, 0x08, 0xc2, 0x79, 0x99, 0x2c, 0x55, 0xef, + 0x4a, 0xbd, + }, +} + +// $ openssl s_server -tls1_2 -cert server.crt -key server.key \ +// -cipher ECDHE-RSA-AES128-SHA -port 10443 +// $ go test -test.run "TestRunClient" -connect -ciphersuites=0xc013 \ +// -minversion=0x0303 -maxversion=0x0303 +var clientTLS12Script = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x54, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x13, + 0x01, 0x00, 0x00, 0x29, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x01, 0x04, + 0x03, 0x02, 0x01, 0x02, 0x03, + }, + { + 0x16, 0x03, 0x03, 0x00, 0x54, 0x02, 0x00, 0x00, + 0x50, 0x03, 0x03, 0x52, 0x65, 0x67, 0xbd, 0xe8, + 0x72, 0x03, 0x6a, 0x52, 0x8d, 0x28, 0x2c, 0x9a, + 0x53, 0xff, 0xc2, 0xa1, 0x62, 0x5f, 0x54, 0xfb, + 0x73, 0x00, 0xcf, 0x4d, 0x28, 0x36, 0xc2, 0xee, + 0xfd, 0x78, 0xf0, 0x20, 0x6f, 0xbe, 0x49, 0xec, + 0x5b, 0x6f, 0xf9, 0x53, 0x42, 0x69, 0x0d, 0x6d, + 0x8b, 0x68, 0x2e, 0xca, 0x3c, 0x3c, 0x88, 0x9e, + 0x8b, 0xf9, 0x32, 0x65, 0x09, 0xd6, 0xa0, 0x7d, + 0xea, 0xc6, 0xd5, 0xc4, 0xc0, 0x13, 0x00, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, + 0x02, 0x16, 0x03, 0x03, 0x02, 0x39, 0x0b, 0x00, + 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, + 0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36, + 0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, + 0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31, + 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, + 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, + 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, + 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, + 0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52, + 0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, + 0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, + 0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, + 0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, + 0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, + 0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, + 0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, + 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, + 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, + 0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, + 0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, + 0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, + 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85, + 0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, + 0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, + 0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, + 0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, + 0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, + 0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, + 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, + 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, + 0x03, 0x03, 0x00, 0x8d, 0x0c, 0x00, 0x00, 0x89, + 0x03, 0x00, 0x17, 0x41, 0x04, 0x48, 0x93, 0x62, + 0x6a, 0xf8, 0x7c, 0x94, 0xcc, 0xcc, 0x0a, 0x9b, + 0x5e, 0x11, 0xad, 0x0b, 0x30, 0xc4, 0x5d, 0xf7, + 0x63, 0x24, 0xc1, 0xb0, 0x40, 0x5f, 0xff, 0x9f, + 0x0d, 0x7e, 0xd5, 0xa5, 0xd0, 0x4f, 0x80, 0x16, + 0xa8, 0x66, 0x18, 0x31, 0x1f, 0x81, 0xb2, 0x9a, + 0x41, 0x62, 0x5b, 0xcf, 0x73, 0xac, 0x4a, 0x64, + 0xb5, 0xc1, 0x46, 0x4d, 0x8a, 0xac, 0x25, 0xba, + 0x81, 0x7f, 0xbe, 0x64, 0x68, 0x04, 0x01, 0x00, + 0x40, 0x4e, 0x3f, 0x1e, 0x04, 0x4c, 0xef, 0xd2, + 0xa6, 0x82, 0xe6, 0x7c, 0x76, 0x23, 0x17, 0xb9, + 0xe7, 0x52, 0x15, 0x6b, 0x3d, 0xb2, 0xb1, 0x17, + 0x7d, 0xe6, 0xde, 0x06, 0x87, 0x30, 0xb0, 0xb5, + 0x57, 0xae, 0xdf, 0xb2, 0xdc, 0x8d, 0xab, 0x76, + 0x9c, 0xaa, 0x45, 0x6d, 0x23, 0x5d, 0xc1, 0xa8, + 0x7b, 0x79, 0x79, 0xb1, 0x3c, 0xdc, 0xf5, 0x33, + 0x2c, 0xa1, 0x62, 0x3e, 0xbd, 0xf5, 0x5d, 0x6c, + 0x87, 0x16, 0x03, 0x03, 0x00, 0x04, 0x0e, 0x00, + 0x00, 0x00, + }, + { + 0x16, 0x03, 0x03, 0x00, 0x46, 0x10, 0x00, 0x00, + 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d, + 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5, + 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd, + 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce, + 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e, + 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56, + 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49, + 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b, + 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x03, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x03, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x17, + 0x54, 0x51, 0xb6, 0x1d, 0x8e, 0xe4, 0x6b, 0xed, + 0x5b, 0xa1, 0x27, 0x7f, 0xdc, 0xa9, 0xa5, 0xcf, + 0x38, 0xe6, 0x5d, 0x17, 0x34, 0xf9, 0xc0, 0x07, + 0xb8, 0xbe, 0x56, 0xe6, 0xd6, 0x6a, 0xb6, 0x26, + 0x4e, 0x45, 0x8d, 0x48, 0xe9, 0xc6, 0xb1, 0xa1, + 0xea, 0xdc, 0xb1, 0x37, 0xd9, 0xf6, + }, + { + 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x03, 0x00, 0x40, 0x00, 0x68, 0xc5, 0x27, 0xd5, + 0x3d, 0xba, 0x04, 0xde, 0x63, 0xf1, 0x5b, 0xc3, + 0x86, 0xb9, 0x82, 0xc7, 0xb3, 0x90, 0x31, 0xea, + 0x15, 0xe1, 0x42, 0x76, 0x7d, 0x90, 0xcb, 0xc9, + 0xd1, 0x05, 0xe6, 0x8c, 0x76, 0xc7, 0x9a, 0x35, + 0x67, 0xa2, 0x70, 0x9a, 0x8a, 0x6c, 0xb5, 0x6b, + 0xc7, 0x87, 0xf3, 0x65, 0x0a, 0xa0, 0x98, 0xba, + 0x57, 0xbb, 0x31, 0x7b, 0x1f, 0x1a, 0xf7, 0x2a, + 0xf3, 0x12, 0xf6, + }, + { + 0x17, 0x03, 0x03, 0x00, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x10, 0x80, + 0x54, 0x1e, 0x72, 0xd3, 0x1a, 0x86, 0x1c, 0xc4, + 0x4a, 0x9b, 0xd4, 0x80, 0xd2, 0x03, 0x35, 0x0d, + 0xe4, 0x12, 0xc2, 0x3d, 0x79, 0x4a, 0x2c, 0xba, + 0xc2, 0xad, 0xf3, 0xd2, 0x16, 0x15, 0x03, 0x03, + 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x9b, 0x68, 0x78, 0x92, 0x28, + 0x62, 0x02, 0x65, 0x87, 0x90, 0xe4, 0x32, 0xd7, + 0x72, 0x08, 0x70, 0xb8, 0x52, 0x32, 0x1f, 0x97, + 0xd4, 0x6a, 0xc6, 0x28, 0x83, 0xb0, 0x1d, 0x6e, + 0x16, 0xd5, + }, +} + +// $ openssl s_server -tls1_2 -cert server.crt -key server.key \ +// -port 10443 -verify 0 +// $ go test -test.run "TestRunClient" -connect -ciphersuites=0xc02f \ +// -maxversion=0x0303 +var clientTLS12ClientCertScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x58, 0x01, 0x00, 0x00, + 0x54, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x2f, + 0x01, 0x00, 0x00, 0x29, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, + 0x0d, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x01, 0x04, + 0x03, 0x02, 0x01, 0x02, 0x03, + }, + { + 0x16, 0x03, 0x03, 0x00, 0x54, 0x02, 0x00, 0x00, + 0x50, 0x03, 0x03, 0x52, 0x65, 0x67, 0xe0, 0xe8, + 0xf1, 0x13, 0x2a, 0x83, 0x28, 0xa8, 0x2e, 0x76, + 0x69, 0xe6, 0x89, 0x55, 0x6c, 0x48, 0x49, 0x2e, + 0x00, 0xf6, 0x87, 0x6c, 0x13, 0xa1, 0xd4, 0xaa, + 0xd0, 0x76, 0x3b, 0x20, 0xe4, 0xd6, 0x5b, 0x1d, + 0x11, 0xf2, 0x42, 0xf2, 0x82, 0x0c, 0x0d, 0x66, + 0x6d, 0xec, 0x52, 0xf8, 0x4a, 0xd9, 0x45, 0xcf, + 0xe4, 0x4a, 0xba, 0x8b, 0xf1, 0xab, 0x55, 0xe4, + 0x57, 0x18, 0xa9, 0x36, 0xc0, 0x2f, 0x00, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, + 0x02, 0x16, 0x03, 0x03, 0x02, 0x39, 0x0b, 0x00, + 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, + 0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, + 0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, + 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, + 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, + 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, + 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, + 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36, + 0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, + 0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31, + 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, + 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, + 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, + 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, + 0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52, + 0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, + 0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, + 0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, + 0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, + 0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, + 0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, + 0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, + 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, + 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, + 0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, + 0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, + 0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, + 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85, + 0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, + 0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, + 0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, + 0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, + 0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, + 0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, + 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, + 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, + 0x03, 0x03, 0x00, 0x8d, 0x0c, 0x00, 0x00, 0x89, + 0x03, 0x00, 0x17, 0x41, 0x04, 0xaa, 0xf0, 0x0c, + 0xa3, 0x60, 0xcf, 0x69, 0x1e, 0xad, 0x16, 0x9a, + 0x01, 0x40, 0xc6, 0x22, 0xc4, 0xbb, 0x06, 0x3b, + 0x84, 0x65, 0xea, 0xc7, 0xa2, 0x96, 0x79, 0x17, + 0x2f, 0xc7, 0xbe, 0x56, 0x39, 0xe4, 0x79, 0xf3, + 0xad, 0x17, 0xf3, 0x7e, 0xe2, 0x7b, 0xa2, 0x6f, + 0x3f, 0x96, 0xea, 0xe5, 0x0e, 0xea, 0x39, 0x79, + 0x77, 0xeb, 0x14, 0x18, 0xbb, 0x7c, 0x95, 0xda, + 0xa7, 0x51, 0x09, 0xba, 0xd7, 0x04, 0x01, 0x00, + 0x40, 0x82, 0x3e, 0xce, 0xee, 0x7e, 0xba, 0x3b, + 0x51, 0xb1, 0xba, 0x71, 0x2e, 0x54, 0xa9, 0xb9, + 0xe2, 0xb1, 0x59, 0x17, 0xa1, 0xac, 0x76, 0xb4, + 0x4e, 0xf1, 0xae, 0x65, 0x17, 0x2b, 0x43, 0x06, + 0x31, 0x29, 0x0b, 0xa0, 0x1e, 0xb6, 0xfa, 0x35, + 0xe8, 0x63, 0x06, 0xde, 0x13, 0x89, 0x83, 0x69, + 0x3b, 0xc2, 0x15, 0x73, 0x1c, 0xc5, 0x07, 0xe9, + 0x38, 0x9b, 0x06, 0x81, 0x1b, 0x97, 0x7c, 0xa6, + 0x89, 0x16, 0x03, 0x03, 0x00, 0x30, 0x0d, 0x00, + 0x00, 0x28, 0x03, 0x01, 0x02, 0x40, 0x00, 0x20, + 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, + 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, + 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, + 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x03, 0x0a, 0xfb, 0x0b, 0x00, 0x0a, + 0xf7, 0x00, 0x0a, 0xf4, 0x00, 0x03, 0x7e, 0x30, + 0x82, 0x03, 0x7a, 0x30, 0x82, 0x02, 0x62, 0x02, + 0x09, 0x00, 0xb4, 0x47, 0x58, 0x57, 0x2b, 0x67, + 0xc8, 0xc2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0x80, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79, + 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79, 0x20, 0x43, + 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63, 0x61, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, + 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, + 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, + 0x34, 0x34, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x34, + 0x34, 0x30, 0x30, 0x5a, 0x30, 0x7d, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x08, 0x4e, 0x65, + 0x77, 0x20, 0x59, 0x6f, 0x72, 0x6b, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, 0x79, + 0x6e, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x07, 0x4d, 0x79, 0x20, 0x4c, + 0x65, 0x61, 0x66, 0x31, 0x13, 0x30, 0x11, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0a, 0x6d, 0x79, + 0x6c, 0x65, 0x61, 0x66, 0x2e, 0x63, 0x6f, 0x6d, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69, + 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa0, 0xa3, 0xef, 0xc1, + 0x44, 0x7d, 0xa2, 0xe3, 0x71, 0x98, 0x27, 0x63, + 0xb3, 0x1d, 0x71, 0x50, 0xa6, 0x34, 0x15, 0xcb, + 0xc9, 0x2a, 0xc3, 0xea, 0xe4, 0x9e, 0x9c, 0x49, + 0xa6, 0x01, 0x9b, 0x7e, 0xa9, 0xb5, 0x7a, 0xff, + 0x15, 0x92, 0x71, 0xc8, 0x97, 0x9c, 0x25, 0xb7, + 0x79, 0x2b, 0xff, 0xab, 0xc6, 0xb1, 0xa7, 0x00, + 0x90, 0xb2, 0x8b, 0xd7, 0x71, 0xd5, 0xc2, 0x3a, + 0xe6, 0x82, 0x42, 0x37, 0x89, 0x41, 0x04, 0xb0, + 0xba, 0xc7, 0x5b, 0x8a, 0x43, 0x9f, 0x97, 0x39, + 0x0c, 0x0f, 0xd5, 0x6d, 0x9e, 0x8d, 0xeb, 0xc0, + 0x26, 0xc5, 0x18, 0xe8, 0x7a, 0x3d, 0x32, 0x2e, + 0x38, 0x90, 0x40, 0x5b, 0x39, 0x2c, 0x07, 0xcb, + 0x24, 0x10, 0xc5, 0xc9, 0x3b, 0xe3, 0x66, 0x47, + 0x57, 0xb9, 0x6a, 0xad, 0x44, 0xf8, 0xd0, 0x70, + 0x62, 0x3b, 0x8e, 0xed, 0x60, 0x5f, 0x22, 0xf8, + 0xb8, 0x0c, 0xc9, 0x41, 0x2b, 0xc9, 0x80, 0x6e, + 0x4e, 0x1b, 0xe1, 0x20, 0xfc, 0x47, 0xa4, 0xac, + 0xc3, 0x3f, 0xe6, 0xc2, 0x81, 0x79, 0x03, 0x37, + 0x25, 0x89, 0xca, 0xd6, 0xa5, 0x46, 0x91, 0x63, + 0x41, 0xc5, 0x3e, 0xd5, 0xed, 0x7f, 0x4f, 0x8d, + 0x06, 0xc0, 0x89, 0x00, 0xbe, 0x37, 0x7b, 0x7e, + 0x73, 0xca, 0x70, 0x00, 0x14, 0x34, 0xbe, 0x47, + 0xbc, 0xb2, 0x6a, 0x28, 0xa5, 0x29, 0x84, 0xa8, + 0x9d, 0xc8, 0x1e, 0x77, 0x66, 0x1f, 0x9f, 0xaa, + 0x2b, 0x47, 0xdb, 0xdd, 0x6b, 0x9c, 0xa8, 0xfc, + 0x82, 0x36, 0x94, 0x62, 0x0d, 0x5c, 0x3f, 0xb2, + 0x01, 0xb4, 0xa5, 0xb8, 0xc6, 0x0e, 0x94, 0x5b, + 0xec, 0x5e, 0xbb, 0x7a, 0x63, 0x24, 0xf1, 0xf9, + 0xd6, 0x50, 0x08, 0xc1, 0xa3, 0xcc, 0x90, 0x07, + 0x5b, 0x04, 0x04, 0x42, 0x74, 0xcf, 0x37, 0xfa, + 0xf0, 0xa5, 0xd9, 0xd3, 0x86, 0x89, 0x89, 0x18, + 0xf3, 0x4c, 0xe2, 0x11, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x90, 0xbb, 0xf9, + 0x5e, 0xba, 0x17, 0x1f, 0xac, 0x21, 0x9f, 0x6b, + 0x4a, 0x46, 0xd0, 0x6d, 0x3c, 0x8f, 0x3d, 0xf8, + 0x5e, 0x3e, 0x72, 0xaf, 0xa0, 0x1a, 0xf3, 0xff, + 0x89, 0xac, 0x5b, 0x7a, 0xe2, 0x91, 0x2a, 0x23, + 0x85, 0xc6, 0x4d, 0x47, 0x67, 0x01, 0x08, 0xa8, + 0x05, 0x1d, 0x01, 0x60, 0x50, 0x5f, 0x59, 0xad, + 0xfe, 0x7b, 0xc6, 0x0c, 0x54, 0x90, 0x68, 0x70, + 0x67, 0x2e, 0xed, 0x87, 0xf8, 0x69, 0x8a, 0xac, + 0x32, 0xfe, 0x6f, 0x90, 0x19, 0x2a, 0x64, 0x8d, + 0x82, 0x66, 0x05, 0x43, 0x88, 0xee, 0xf2, 0x30, + 0xed, 0xa4, 0x8f, 0xbf, 0xd6, 0x57, 0x20, 0xd4, + 0x43, 0x1d, 0x52, 0x96, 0x6f, 0xae, 0x09, 0x96, + 0x01, 0x52, 0x38, 0xe3, 0xaf, 0x99, 0xd7, 0xdc, + 0x14, 0x99, 0xc4, 0x8b, 0x0e, 0x04, 0x0f, 0xb3, + 0x14, 0x14, 0xd4, 0xa5, 0x93, 0xe1, 0xc9, 0x8a, + 0x81, 0xef, 0x63, 0xfc, 0x36, 0x77, 0x05, 0x06, + 0xf0, 0x2a, 0x04, 0x0a, 0xbe, 0x2e, 0xce, 0x81, + 0x3d, 0x23, 0xa1, 0xda, 0xd8, 0xeb, 0xc6, 0xea, + 0x5e, 0xcf, 0x28, 0x36, 0x51, 0x31, 0x95, 0x5e, + 0x40, 0x04, 0xed, 0xac, 0xc1, 0xc8, 0x56, 0x69, + 0x87, 0xec, 0x3b, 0x03, 0x3e, 0x9d, 0x0f, 0x4c, + 0x4c, 0xeb, 0xd7, 0xba, 0x26, 0xdf, 0xe3, 0xde, + 0x10, 0xee, 0x93, 0x62, 0x8d, 0x73, 0x52, 0x6e, + 0xff, 0x37, 0x36, 0x98, 0x7b, 0x2d, 0x56, 0x4c, + 0xba, 0x09, 0xb8, 0xa7, 0xf0, 0x3b, 0x16, 0x81, + 0xca, 0xdb, 0x43, 0xab, 0xec, 0x4c, 0x6e, 0x7c, + 0xc1, 0x0b, 0x22, 0x22, 0x43, 0x1d, 0xb6, 0x0c, + 0xc1, 0xb9, 0xcf, 0xe4, 0x53, 0xee, 0x1d, 0x3e, + 0x88, 0xa7, 0x13, 0xbe, 0x7f, 0xbd, 0xae, 0x72, + 0xcf, 0xcd, 0x63, 0xd2, 0xc3, 0x18, 0x58, 0x92, + 0xa2, 0xad, 0xb5, 0x09, 0x9d, 0x91, 0x03, 0xdd, + 0x3c, 0xe2, 0x1c, 0xde, 0x78, 0x00, 0x03, 0x88, + 0x30, 0x82, 0x03, 0x84, 0x30, 0x82, 0x02, 0x6c, + 0x02, 0x09, 0x00, 0xab, 0xed, 0xa6, 0xe4, 0x4a, + 0x2b, 0x2b, 0xf8, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, + 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, + 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69, + 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x31, + 0x38, 0x34, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x33, + 0x30, 0x36, 0x32, 0x35, 0x32, 0x31, 0x31, 0x38, + 0x34, 0x30, 0x5a, 0x30, 0x81, 0x80, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, + 0x6c, 0x79, 0x6e, 0x31, 0x15, 0x30, 0x13, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0c, 0x4d, 0x79, + 0x20, 0x43, 0x41, 0x20, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0e, 0x6d, 0x79, 0x63, + 0x61, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x01, 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, + 0x61, 0x68, 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, + 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, + 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, + 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xce, + 0x13, 0xf0, 0x72, 0xb0, 0x61, 0xc8, 0x18, 0x37, + 0x8a, 0x41, 0x3d, 0x20, 0xa1, 0x1c, 0xcb, 0xbf, + 0xf6, 0x3b, 0x74, 0x26, 0x2a, 0x96, 0x11, 0xec, + 0x53, 0xa1, 0xcc, 0x7d, 0x77, 0x56, 0x45, 0x0f, + 0x36, 0xb7, 0xf2, 0x48, 0x92, 0x1a, 0x62, 0xcc, + 0xb6, 0xc0, 0xa1, 0x2f, 0x44, 0x2b, 0xc1, 0x89, + 0xcb, 0x6e, 0x1e, 0xdb, 0x57, 0x92, 0xd5, 0x97, + 0x60, 0x8c, 0x41, 0x2c, 0xd9, 0x20, 0xfe, 0xe9, + 0x1f, 0x8e, 0xfc, 0x7f, 0x02, 0x44, 0x0f, 0x28, + 0x81, 0xd6, 0x0c, 0xcd, 0xbc, 0xf0, 0x57, 0x6c, + 0xcc, 0xa7, 0xba, 0x06, 0xa0, 0xa6, 0x91, 0xda, + 0xef, 0x46, 0x8a, 0x60, 0x0f, 0x52, 0x6c, 0x90, + 0x6c, 0x8c, 0x44, 0xaf, 0xb0, 0x9d, 0x90, 0xba, + 0x21, 0x58, 0xa0, 0x3c, 0xee, 0x54, 0xb5, 0x29, + 0x26, 0x1f, 0x0a, 0xac, 0xef, 0x48, 0x68, 0x33, + 0xd0, 0x33, 0xd0, 0x8b, 0x1a, 0xec, 0x6e, 0x2f, + 0xb5, 0x4a, 0x53, 0xc2, 0x1a, 0xd2, 0xf1, 0x50, + 0x05, 0x59, 0x5c, 0xd9, 0xda, 0x03, 0x0a, 0x47, + 0xb7, 0xdd, 0xf7, 0x3a, 0x69, 0xf5, 0x4e, 0xea, + 0x4a, 0xc2, 0xca, 0x54, 0xb0, 0x8b, 0x76, 0xe1, + 0x02, 0x2d, 0x52, 0x67, 0xb9, 0xdd, 0x50, 0xc9, + 0x3b, 0x07, 0x24, 0x22, 0x6a, 0x00, 0x1d, 0x58, + 0x83, 0xa8, 0xec, 0x95, 0xf1, 0xda, 0xe2, 0x73, + 0xa0, 0xa1, 0x72, 0x60, 0x9e, 0x86, 0x53, 0xcb, + 0x45, 0xa8, 0xc2, 0xa0, 0x50, 0xa0, 0x53, 0xd6, + 0xfc, 0x18, 0x84, 0xb5, 0x4a, 0x26, 0xd0, 0xa2, + 0xaa, 0xd0, 0xff, 0xb6, 0xfe, 0x3a, 0x9c, 0xb5, + 0x19, 0x3b, 0x3f, 0xe1, 0x48, 0x0d, 0xa4, 0x09, + 0x4f, 0x83, 0xc9, 0xc0, 0xc9, 0xa6, 0x0b, 0x58, + 0x1f, 0x1c, 0x7b, 0xac, 0xa2, 0x42, 0xbc, 0x61, + 0xf4, 0x21, 0x8a, 0x00, 0xda, 0x14, 0xa0, 0x60, + 0x03, 0xfe, 0x93, 0x12, 0x6c, 0x56, 0xcd, 0x02, + 0x03, 0x01, 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x25, 0x29, 0x3b, 0x1e, 0xc3, 0x58, 0x32, 0xe6, + 0x23, 0xc8, 0xee, 0x18, 0xf0, 0x1d, 0x62, 0x6d, + 0x3b, 0x59, 0x99, 0x3a, 0xfe, 0x49, 0x72, 0x07, + 0x3f, 0x58, 0x93, 0xdb, 0xc0, 0xaf, 0xb0, 0xb3, + 0x5c, 0xd1, 0x5c, 0x98, 0xc8, 0xea, 0x4a, 0xe4, + 0x58, 0x73, 0x0d, 0x57, 0xc5, 0x13, 0x7c, 0x5c, + 0x79, 0x66, 0xda, 0x04, 0x1d, 0xe5, 0x98, 0xda, + 0x35, 0x47, 0x44, 0xb0, 0xd2, 0x7a, 0x66, 0x9d, + 0xcd, 0x41, 0xa5, 0x8f, 0xa1, 0x11, 0xb2, 0x1a, + 0x87, 0xc0, 0xcd, 0x55, 0xed, 0xb4, 0x7b, 0x33, + 0x72, 0xeb, 0xf7, 0xe3, 0x7b, 0x8b, 0x02, 0x86, + 0xe9, 0x2b, 0x26, 0x32, 0x9f, 0x99, 0xf1, 0xcb, + 0x93, 0xab, 0xb9, 0x16, 0xb3, 0x9a, 0xb2, 0x22, + 0x13, 0x21, 0x1f, 0x5b, 0xcc, 0xa2, 0x59, 0xbb, + 0x69, 0xf2, 0xb8, 0x07, 0x80, 0xce, 0x0c, 0xf7, + 0x98, 0x4c, 0x85, 0xc2, 0x96, 0x6a, 0x22, 0x05, + 0xe9, 0xbe, 0x48, 0xb0, 0x02, 0x5b, 0x69, 0x28, + 0x18, 0x88, 0x96, 0xe3, 0xd7, 0xc6, 0x7a, 0xd3, + 0xe9, 0x99, 0xff, 0x9d, 0xc3, 0x61, 0x4d, 0x9a, + 0x96, 0xf2, 0xc6, 0x33, 0x4d, 0xe5, 0x5d, 0x5a, + 0x68, 0x64, 0x5a, 0x82, 0x35, 0x65, 0x25, 0xe3, + 0x8c, 0x5b, 0xb0, 0xf6, 0x96, 0x56, 0xbc, 0xbf, + 0x97, 0x76, 0x4b, 0x66, 0x44, 0x81, 0xa4, 0xc4, + 0xa7, 0x31, 0xc5, 0xa1, 0x4f, 0xe8, 0xa4, 0xca, + 0x20, 0xf5, 0x01, 0x5b, 0x99, 0x4f, 0x5a, 0xf4, + 0xf0, 0x78, 0xbf, 0x71, 0x49, 0xd5, 0xf1, 0xc1, + 0xa2, 0x18, 0xfd, 0x72, 0x5b, 0x16, 0xe8, 0x92, + 0xc7, 0x37, 0x48, 0xaf, 0xee, 0x24, 0xfc, 0x35, + 0x0b, 0xc2, 0xdd, 0x05, 0xc7, 0x6e, 0xa3, 0x29, + 0xbb, 0x29, 0x7d, 0xd3, 0x2b, 0x94, 0x80, 0xc3, + 0x40, 0x53, 0x0e, 0x03, 0x54, 0x3d, 0x7b, 0x8b, + 0xce, 0xf9, 0xa4, 0x03, 0x27, 0x63, 0xec, 0x51, + 0x00, 0x03, 0xe5, 0x30, 0x82, 0x03, 0xe1, 0x30, + 0x82, 0x02, 0xc9, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x09, 0x00, 0xcc, 0x22, 0x4c, 0x4b, 0x98, + 0xa2, 0x88, 0xfc, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x30, 0x81, 0x86, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, 0x31, + 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, 0x6c, + 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, + 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x08, + 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, 0x67, + 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, 0x69, + 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, 0x2e, + 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x33, 0x30, 0x35, 0x32, 0x36, 0x32, 0x31, 0x30, + 0x35, 0x30, 0x31, 0x5a, 0x17, 0x0d, 0x32, 0x33, + 0x30, 0x35, 0x32, 0x34, 0x32, 0x31, 0x30, 0x35, + 0x30, 0x31, 0x5a, 0x30, 0x81, 0x86, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x02, 0x4e, 0x59, + 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, + 0x07, 0x0c, 0x08, 0x42, 0x72, 0x6f, 0x6f, 0x6b, + 0x6c, 0x79, 0x6e, 0x31, 0x21, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x4d, 0x79, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, + 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x08, 0x6d, 0x79, 0x63, 0x61, 0x2e, 0x6f, 0x72, + 0x67, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, + 0x16, 0x12, 0x6a, 0x76, 0x73, 0x68, 0x61, 0x68, + 0x69, 0x64, 0x40, 0x67, 0x6d, 0x61, 0x69, 0x6c, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xf0, 0xfb, 0xad, + 0x80, 0x5e, 0x37, 0xd3, 0x6d, 0xee, 0x2e, 0xcc, + 0xbc, 0x0c, 0xd7, 0x56, 0x4b, 0x56, 0x45, 0xcd, + 0x28, 0xb6, 0x22, 0xe9, 0xe2, 0x0f, 0xd1, 0x87, + 0x2a, 0x27, 0xce, 0x77, 0x8d, 0x6e, 0x0e, 0x0f, + 0xfb, 0x66, 0xe1, 0xb5, 0x0e, 0x9a, 0xb6, 0x05, + 0x8e, 0xb3, 0xe1, 0xc5, 0x77, 0x86, 0x5b, 0x46, + 0xd2, 0x0b, 0x92, 0x03, 0x1b, 0x89, 0x0c, 0x1b, + 0x10, 0x0e, 0x99, 0x8f, 0xe2, 0x17, 0xe8, 0xc2, + 0x30, 0x00, 0x47, 0xd6, 0xfc, 0xf9, 0x0f, 0x3b, + 0x75, 0x34, 0x8d, 0x4d, 0xb0, 0x99, 0xb7, 0xa0, + 0x6d, 0xa0, 0xb6, 0xad, 0xda, 0x07, 0x5e, 0x38, + 0x2e, 0x02, 0xe4, 0x30, 0x6d, 0xae, 0x13, 0x72, + 0xd4, 0xc8, 0xce, 0x14, 0x07, 0xae, 0x23, 0x8c, + 0x8f, 0x9e, 0x8c, 0x60, 0xd6, 0x06, 0xb9, 0xef, + 0x00, 0x18, 0xc0, 0x1d, 0x25, 0x1e, 0xda, 0x3e, + 0x2f, 0xcf, 0x2b, 0x56, 0x84, 0x9e, 0x30, 0x21, + 0xc7, 0x29, 0xf6, 0x03, 0x8a, 0x24, 0xf9, 0x34, + 0xac, 0x65, 0x9d, 0x80, 0x36, 0xc8, 0x3b, 0x15, + 0x10, 0xbd, 0x51, 0xe9, 0xbc, 0x02, 0xe1, 0xe9, + 0xb3, 0x5a, 0x9a, 0x99, 0x41, 0x1b, 0x27, 0xa0, + 0x4d, 0x50, 0x9e, 0x27, 0x7f, 0xa1, 0x7d, 0x09, + 0x87, 0xbd, 0x8a, 0xca, 0x5f, 0xb1, 0xa5, 0x08, + 0xb8, 0x04, 0xd4, 0x52, 0x89, 0xaa, 0xe0, 0x7d, + 0x42, 0x2e, 0x2f, 0x15, 0xee, 0x66, 0x57, 0x0f, + 0x13, 0x19, 0x45, 0xa8, 0x4b, 0x5d, 0x81, 0x66, + 0xcc, 0x12, 0x37, 0x94, 0x5e, 0xfd, 0x3c, 0x10, + 0x81, 0x51, 0x3f, 0xfa, 0x0f, 0xdd, 0xa1, 0x89, + 0x03, 0xa9, 0x78, 0x91, 0xf5, 0x3b, 0xf3, 0xbc, + 0xac, 0xbe, 0x93, 0x30, 0x2e, 0xbe, 0xca, 0x7f, + 0x46, 0xd3, 0x28, 0xb4, 0x4e, 0x91, 0x7b, 0x5b, + 0x43, 0x6c, 0xaf, 0x9b, 0x5c, 0x6a, 0x6d, 0x5a, + 0xdb, 0x79, 0x5e, 0x6a, 0x6b, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, + 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa, 0x7d, + 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c, 0x90, + 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x6b, 0x1e, 0x00, 0xa8, 0x9f, 0xfa, + 0x7d, 0x00, 0xf9, 0xe0, 0x9d, 0x0f, 0x90, 0x8c, + 0x90, 0xa8, 0xa1, 0x37, 0x6b, 0xda, 0x30, 0x0c, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, + 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0xcd, 0x6f, 0x73, 0x4d, 0x56, 0x0b, 0xf3, 0x2e, + 0x1c, 0xe2, 0x02, 0x0c, 0x14, 0xbb, 0x2f, 0xdd, + 0x3c, 0x43, 0xfe, 0xdf, 0x94, 0x2d, 0xa9, 0x89, + 0x81, 0x51, 0xf8, 0x5f, 0xa7, 0xa0, 0x13, 0xaa, + 0xcc, 0xb0, 0x18, 0xe2, 0x57, 0x3e, 0x0d, 0x29, + 0x93, 0xe8, 0x95, 0xd5, 0x1b, 0x53, 0xd2, 0x51, + 0xf2, 0xbd, 0xf5, 0x9e, 0x7b, 0x22, 0x65, 0x62, + 0x5c, 0xc4, 0x4c, 0x1d, 0xe8, 0xe9, 0xc3, 0xd4, + 0x2b, 0xe7, 0x78, 0xcb, 0x10, 0xf3, 0xfe, 0x06, + 0x83, 0xdc, 0x3a, 0x1e, 0x62, 0x10, 0xc0, 0x46, + 0x77, 0xc6, 0x9d, 0x9f, 0xab, 0x96, 0x25, 0x5c, + 0xfb, 0x26, 0xc1, 0x15, 0x1f, 0xa5, 0x33, 0xee, + 0x4f, 0x9a, 0x14, 0x6a, 0x14, 0x97, 0x93, 0x2b, + 0x95, 0x0b, 0xdc, 0xa8, 0xd7, 0x69, 0x2e, 0xf0, + 0x01, 0x0e, 0xfd, 0x4e, 0xd0, 0xd9, 0xa8, 0xe5, + 0x65, 0xde, 0xfb, 0xca, 0xca, 0x1c, 0x5f, 0xf9, + 0x53, 0xa0, 0x87, 0xe7, 0x33, 0x9b, 0x2f, 0xcf, + 0xe4, 0x13, 0xfc, 0xec, 0x7a, 0x6c, 0xb0, 0x90, + 0x13, 0x9b, 0xb6, 0xc5, 0x03, 0xf6, 0x0e, 0x5e, + 0xe2, 0xe4, 0x26, 0xc1, 0x7e, 0x53, 0xfe, 0x69, + 0xa3, 0xc7, 0xd8, 0x8e, 0x6e, 0x94, 0x32, 0xa0, + 0xde, 0xca, 0xb6, 0xcc, 0xd6, 0x01, 0xd5, 0x78, + 0x40, 0x28, 0x63, 0x9b, 0xee, 0xcf, 0x09, 0x3b, + 0x35, 0x04, 0xf0, 0x14, 0x02, 0xf6, 0x80, 0x0e, + 0x90, 0xb2, 0x94, 0xd2, 0x25, 0x16, 0xb8, 0x7a, + 0x76, 0x87, 0x84, 0x9f, 0x84, 0xc5, 0xaf, 0xc2, + 0x6d, 0x68, 0x7a, 0x84, 0x9c, 0xc6, 0x8a, 0x63, + 0x60, 0x87, 0x6a, 0x25, 0xc1, 0xa1, 0x78, 0x0f, + 0xba, 0xe8, 0x5f, 0xe1, 0xba, 0xac, 0xa4, 0x6f, + 0xdd, 0x09, 0x3f, 0x12, 0xcb, 0x1d, 0xf3, 0xcf, + 0x48, 0xd7, 0xd3, 0x26, 0xe8, 0x9c, 0xc3, 0x53, + 0xb3, 0xba, 0xdc, 0x32, 0x99, 0x98, 0x96, 0xd6, + 0x16, 0x03, 0x03, 0x00, 0x46, 0x10, 0x00, 0x00, + 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d, + 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5, + 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd, + 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce, + 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e, + 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56, + 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49, + 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b, + 0xdc, 0x5a, 0x89, 0x16, 0x03, 0x03, 0x01, 0x08, + 0x0f, 0x00, 0x01, 0x04, 0x04, 0x01, 0x01, 0x00, + 0x7e, 0xe4, 0x65, 0x02, 0x8e, 0xb3, 0x34, 0x6a, + 0x47, 0x71, 0xd1, 0xb0, 0x8d, 0x3c, 0x0c, 0xe1, + 0xde, 0x7e, 0x5f, 0xb4, 0x15, 0x2d, 0x32, 0x0a, + 0x2a, 0xdb, 0x9b, 0x40, 0xba, 0xce, 0x8b, 0xf5, + 0x74, 0xc1, 0x68, 0x20, 0x7c, 0x87, 0x23, 0x13, + 0xc3, 0x13, 0xa7, 0xdb, 0xec, 0x59, 0xa0, 0x40, + 0x9e, 0x64, 0x03, 0x60, 0xac, 0x76, 0xff, 0x01, + 0x34, 0x7b, 0x32, 0x26, 0xd9, 0x41, 0x31, 0x93, + 0xaa, 0x30, 0x51, 0x83, 0x85, 0x40, 0xeb, 0x4e, + 0x66, 0x39, 0x83, 0xb1, 0x30, 0x0d, 0x96, 0x01, + 0xee, 0x81, 0x53, 0x5e, 0xec, 0xa9, 0xc9, 0xdf, + 0x7e, 0xc1, 0x09, 0x47, 0x8b, 0x35, 0xdb, 0x10, + 0x15, 0xd4, 0xc7, 0x5a, 0x39, 0xe3, 0xc0, 0xf3, + 0x93, 0x38, 0x11, 0xdc, 0x71, 0xbb, 0xc7, 0x62, + 0x2b, 0x85, 0xad, 0x6b, 0x4f, 0x09, 0xb3, 0x31, + 0xa8, 0xe5, 0xd1, 0xb3, 0xa9, 0x21, 0x37, 0x50, + 0xc8, 0x7d, 0xc3, 0xd2, 0xf7, 0x00, 0xd3, 0xdb, + 0x0f, 0x82, 0xf2, 0x43, 0xcf, 0x36, 0x6c, 0x98, + 0x63, 0xd8, 0x1d, 0xb3, 0xf3, 0xde, 0x63, 0x79, + 0x64, 0xf0, 0xdb, 0x46, 0x04, 0xe1, 0x1c, 0x57, + 0x0f, 0x9e, 0x96, 0xb9, 0x93, 0x45, 0x71, 0x1c, + 0x8b, 0x65, 0x7d, 0x1e, 0xad, 0xbd, 0x03, 0x51, + 0xae, 0x44, 0xef, 0x97, 0x45, 0x0d, 0x8d, 0x41, + 0x5c, 0x80, 0x7b, 0xe6, 0xe0, 0xbc, 0xa6, 0x72, + 0x95, 0xa0, 0x97, 0xe1, 0xbb, 0xc0, 0xcc, 0xe5, + 0x1e, 0xc3, 0xbe, 0xd7, 0x42, 0x2a, 0xf3, 0x75, + 0x8a, 0x44, 0x67, 0x3c, 0xe5, 0x68, 0x78, 0xe5, + 0x40, 0x1f, 0xf0, 0x89, 0x57, 0xda, 0xee, 0x45, + 0xf4, 0x44, 0x81, 0x01, 0x77, 0xf0, 0x4a, 0x14, + 0xb1, 0x3f, 0x60, 0x2b, 0xeb, 0x42, 0x38, 0xa6, + 0xfb, 0xe5, 0x4d, 0x71, 0xdc, 0x7d, 0x0a, 0x72, + 0x56, 0x28, 0x9d, 0xa6, 0x8e, 0x74, 0x2d, 0xbd, + 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x03, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x31, 0x4d, 0x58, 0x94, 0x0b, + 0x0b, 0x06, 0x5f, 0xae, 0x57, 0x17, 0x98, 0x86, + 0xaa, 0x49, 0x17, 0x7f, 0xbd, 0x41, 0x05, 0xa5, + 0x74, 0x1c, 0x58, 0xc8, 0x38, 0x2d, 0x99, 0x5d, + 0xe5, 0x12, 0x43, + }, + { + 0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x03, 0x00, 0x28, 0xf2, 0x60, 0xc2, 0x75, 0x27, + 0x64, 0xf4, 0x05, 0x98, 0xc9, 0xd3, 0xa8, 0x00, + 0x4c, 0xa0, 0x49, 0x82, 0x68, 0xf1, 0x21, 0x05, + 0x7b, 0x4b, 0x25, 0x3e, 0xe1, 0x5f, 0x0f, 0x84, + 0x26, 0x2d, 0x16, 0x2e, 0xc0, 0xfd, 0xdf, 0x0a, + 0xf4, 0xba, 0x19, + }, + { + 0x17, 0x03, 0x03, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x35, 0xef, 0x9d, + 0x6a, 0x86, 0x98, 0xc5, 0xca, 0x55, 0xca, 0x89, + 0x29, 0xb4, 0x55, 0xd4, 0x41, 0x08, 0x96, 0xe0, + 0xf3, 0x39, 0xfc, 0x15, 0x03, 0x03, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x02, 0x63, 0x1b, 0xaa, 0xc6, 0xc9, 0x6d, 0x72, + 0x24, 0x10, 0x55, 0xa9, 0x8c, 0x3b, 0x23, 0xce, + 0xd8, 0x4a, + }, +} + +var testClientChainCertificate = fromHex( + "2d2d2d2d2d424547494e2050524956415445204b" + + "45592d2d2d2d2d0a4d494945766749424144414e" + + "42676b71686b6947397730424151454641415343" + + "424b67776767536b41674541416f494241514367" + + "6f2b2f4252483269343347590a4a324f7a485846" + + "51706a515679386b71772b726b6e70784a706747" + + "6266716d31657638566b6e48496c35776c74336b" + + "722f367647736163416b4c4b4c313348560a776a" + + "726d676b493369554545734c7248573470446e35" + + "633544412f56625a364e3638416d78526a6f656a" + + "30794c6a6951514673354c41664c4a4244467954" + + "766a0a5a6b64587557717452506a51634749376a" + + "75316758794c3475417a4a5153764a6747354f47" + + "2b45672f45656b724d4d2f35734b4265514d334a" + + "596e4b317156470a6b574e427854375637583950" + + "6a5162416951432b4e33742b6338707741425130" + + "766b6538736d6f6f70536d45714a3349486e646d" + + "48352b714b306662335775630a715079434e7052" + + "694456772f7367473070626a4744705262374636" + + "37656d4d6b38666e5755416a426f387951423173" + + "4542454a307a7a6636384b585a3034614a0a6952" + + "6a7a544f495241674d424141454367674542414a" + + "4b613676326b5a3144596146786e586d7369624c" + + "386734426f67514c6a42307362524a6d746b6b4d" + + "54370a685343325873537551522f446c654d7148" + + "664555786731784a717579597643544d44585972" + + "473667354a5051744d4432465a424a7239626c65" + + "467138386c706a0a543766514e793571354c2b4f" + + "682f6b62433835436e623641753641656978776d" + + "2b6e77665a4f3766726b6278306d35516b715975" + + "5739392f452b69502b454e570a76396a68773436" + + "76515065563236494b79717656462b4f7362722f" + + "6152316138707948336361566e3579594a433346" + + "5855756c6f5a77516331714a6b4c434c4c0a375a" + + "49744f525a78514c486d4d4a654d44722f5a4942" + + "34675467645650636145375a4d5141714d6d3066" + + "4c6b6d7671723149526b77642f6831455a645650" + + "79320a742f6b6b43413039566336663749556575" + + "6f67706d705a50303130564e376b6277394a6348" + + "75544561564543675945417a47395679426e6d62" + + "6858496c57764f0a71583747524f2f5231636a2b" + + "6b564e35377876674b54756b35592b7a4d774a48" + + "32626c57435945513251753974446c476854756b" + + "664273385746772b6e6263460a7a6f706d535245" + + "6c6d464d2f6141536d464733574e5a7072696a68" + + "504b77726338376470636b31703131635a415478" + + "5a413168566d43743457616343673634690a4d74" + + "64507a334e2f34416147664956794d2b69624949" + + "35332f515543675945417953693556735a356f6a" + + "644a795077426e6c6142554231686f2b336b7068" + + "70770a7264572b2b4d796b51494a345564534437" + + "3052486e5a315839754359713978616671746c51" + + "664c44395963442f436d665264706461586c5673" + + "5249467a5a556c0a454630557149644e77337046" + + "68634f4a6d6e5a3241434470434342476f763542" + + "6e3068302b3137686a4b376f69315833716e4542" + + "7857326c7462593476556a500a44394c5330666e" + + "4a76703043675942504a527330714c4a4a464333" + + "6669796b712f57574d38727474354b364a584b50" + + "734b674b53644144577a7463316645434d0a7a65" + + "2b394a6a5a376b4d77557063666a644c2b745047" + + "3455563048326c524375635735414131396d7058" + + "50367454494733713737655a6b416e65516f6163" + + "41340a716c3073583051476c6a5763414e30464b" + + "6f4759733975582b6378445a6e7265362f52392f" + + "3930567766443237454c57546373677734633463" + + "514b42675143420a6f5432326e745a5a59396d6e" + + "72455a36752f492f4a332f35664e396737783733" + + "3177746e463745745a5361575453587364597256" + + "466b564f6362505135494a6f0a714a6a7249372b" + + "474a4d69376f6a4c69642f4c45656f31764f3163" + + "454158334f43723236554e38612f6c7434394f5a" + + "69354c337348556b756c475951755671650a6737" + + "6e6e4632437749544c34503645486443575a4461" + + "7a4136626d7375524f2b6462536e335a6c567651" + + "4b42674859524c5a665458536c44755264776977" + + "746b0a513148546b6d6b57694156726c4f577864" + + "5858456d546130303045574c46446145797a7358" + + "7834424863357166776b5a4e746b634a56396e58" + + "63536e647441530a35767a427a676e797a4f7962" + + "68315878484a3966427472414f3847555878446c" + + "6634394457616753393449763072596e616b7656" + + "2f673039786875415763366e0a5365757230576b" + + "5376453847666653734d485149584c456b0a2d2d" + + "2d2d2d454e442050524956415445204b45592d2d" + + "2d2d2d0a2d2d2d2d2d424547494e204345525449" + + "4649434154452d2d2d2d2d0a4d494944656a4343" + + "416d494343514330523168584b326649776a414e" + + "42676b71686b6947397730424151554641444342" + + "6744454c4d416b474131554542684d430a56564d" + + "78437a414a42674e564241674d416b355a4d5245" + + "77447759445651514844416843636d3976613278" + + "35626a45564d424d47413155454367774d54586b" + + "670a51304567513278705a5735304d5263774651" + + "5944565151444441357465574e68593278705a57" + + "35304c6d4e76625445684d423847435371475349" + + "62334451454a0a41525953616e5a7a6147466f61" + + "5752415a32316861577775593239744d42345844" + + "54457a4d4455794e6a49784e4451774d466f5844" + + "54457a4d4459794e5449780a4e4451774d466f77" + + "6654454c4d416b474131554542684d4356564d78" + + "4554415042674e564241674d4345356c6479425a" + + "62334a724d52457744775944565151480a444168" + + "43636d397661327835626a45514d413447413155" + + "454367774854586b67544756685a6a45544d4245" + + "47413155454177774b62586c735a57466d4c6d4e" + + "760a625445684d42384743537147534962334451" + + "454a41525953616e5a7a6147466f615752415a32" + + "316861577775593239744d494942496a414e4267" + + "6b71686b69470a397730424151454641414f4341" + + "5138414d49494243674b43415145416f4b507677" + + "5552396f754e786d43646a73783178554b593046" + + "63764a4b735071354a36630a536159426d333670" + + "7458722f465a4a78794a65634a6264354b2f2b72" + + "7872476e414a43796939647831634936356f4a43" + + "4e346c42424c43367831754b51352b580a4f5177" + + "50315732656a6576414a73555936486f394d6934" + + "346b4542624f5377487979515178636b3734325a" + + "4856376c7172555434304842694f343774594638" + + "690a2b4c674d7955457279594275546876684950" + + "7848704b7a44502b624367586b444e79574a7974" + + "616c5270466a5163552b3165312f543430477749" + + "6b41766a64370a666e504b634141554e4c354876" + + "4c4a714b4b5570684b6964794235335a682b6671" + + "697448323931726e4b6a38676a61555967316350" + + "374942744b5734786736550a572b78657533706a" + + "4a504835316c41497761504d6b41646242415243" + + "644d38332b76436c32644f4769596b5938307a69" + + "45514944415141424d413047435371470a534962" + + "3344514542425155414134494241514351752f6c" + + "65756863667243476661307047304730386a7a33" + + "34586a357972364161382f2b4a72467436347045" + + "710a493458475455646e4151696f425230425946" + + "42665761332b6538594d564a426f634763753759" + + "6634615971734d7635766b426b715a4932435a67" + + "5644694f37790a4d4f326b6a372f575679445551" + + "7831536c6d2b75435a5942556a6a6a72356e5833" + + "42535a7849734f42412b7a46425455705a506879" + + "597142373250384e6e63460a427641714241712b" + + "4c73364250534f6832746a72787570657a796732" + + "55544756586b414537617a4279465a70682b7737" + + "417a36644430784d363965364a742f6a0a336844" + + "756b324b4e63314a752f7a63326d487374566b79" + + "364362696e384473576763726251367673544735" + + "3877517369496b4d6474677a4275632f6b552b34" + + "640a506f696e4537352f766135797a38316a3073" + + "4d59574a4b697262554a6e5a454433547a69484e" + + "35340a2d2d2d2d2d454e44204345525449464943" + + "4154452d2d2d2d2d0a2d2d2d2d2d424547494e20" + + "43455254494649434154452d2d2d2d2d0a4d4949" + + "4468444343416d7743435143723761626b536973" + + "722b44414e42676b71686b694739773042415155" + + "4641444342686a454c4d416b474131554542684d" + + "430a56564d78437a414a42674e564241674d416b" + + "355a4d524577447759445651514844416843636d" + + "397661327835626a45684d423847413155454367" + + "775954586b670a5132567964476c6d61574e6864" + + "4755675158563061473979615852354d52457744" + + "775944565151444441687465574e684c6d39795a" + + "7a45684d423847435371470a534962334451454a" + + "41525953616e5a7a6147466f615752415a323168" + + "61577775593239744d4234584454457a4d445579" + + "4e6a49784d5467304d466f584454457a0a4d4459" + + "794e5449784d5467304d466f7767594178437a41" + + "4a42674e5642415954416c56544d517377435159" + + "445651514944414a4f575445524d413847413155" + + "450a42777749516e4a7662327473655734784654" + + "415442674e5642416f4d4445313549454e424945" + + "4e7361575675644445584d425547413155454177" + + "774f62586c6a0a59574e73615756756443356a62" + + "3230784954416642676b71686b69473977304243" + + "514557456d70326332686861476c6b5147647459" + + "576c734c6d4e76625443430a415349774451594a" + + "4b6f5a496876634e415145424251414467674550" + + "4144434341516f4367674542414d345438484b77" + + "596367594e34704250534368484d752f0a396a74" + + "304a697157456578546f63783964315a46447a61" + + "33386b6953476d4c4d747343684c30517277596e" + + "4c6268376256354c566c32434d51537a5a495037" + + "700a4834373866774a454479694231677a4e7650" + + "4258624d796e75676167707048613730614b5941" + + "3953624a42736a455376734a3251756946596f44" + + "7a75564c55700a4a68384b724f3949614450514d" + + "39434c477578754c37564b553849613076465142" + + "566c6332646f44436b6533336663366166564f36" + + "6b7243796c5377693362680a416931535a376e64" + + "554d6b37427951696167416457494f6f374a5878" + + "32754a7a6f4b4679594a364755387446714d4b67" + + "554b425431767759684c564b4a7443690a717444" + + "2f747634366e4c555a4f7a2f685341326b43552b" + + "447963444a7067745948787837724b4a43764748" + + "3049596f41326853675941502b6b784a73567330" + + "430a417745414154414e42676b71686b69473977" + + "30424151554641414f43415145414a536b374873" + + "4e594d75596a794f3459384231696254745a6d54" + + "722b535849480a5031695432384376734c4e6330" + + "567959794f704b3546687a445666464533786365" + + "5762614242336c6d4e6f3152305377306e706d6e" + + "63314270592b68456249610a6838444e56653230" + + "657a4e79362f666a6534734368756b724a6a4b66" + + "6d66484c6b36753546724f617369495449523962" + + "7a4b4a5a75326e79754165417a677a330a6d4579" + + "4677705a7149675870766b6977416c74704b4269" + + "496c755058786e7254365a6e2f6e634e68545a71" + + "573873597a54655664576d686b576f49315a5358" + + "6a0a6a46757739705a57764c2b58646b746d5249" + + "476b784b637878614650364b544b495055425735" + + "6c5057765477654c397853645878776149592f58" + + "4a62467569530a787a6449722b346b2f44554c77" + + "7430467832366a4b62737066644d726c49444451" + + "464d4f413151396534764f2b6151444a32507355" + + "513d3d0a2d2d2d2d2d454e442043455254494649" + + "434154452d2d2d2d2d0a2d2d2d2d2d424547494e" + + "2043455254494649434154452d2d2d2d2d0a4d49" + + "49443454434341736d67417749424167494a414d" + + "7769544575596f6f6a384d413047435371475349" + + "623344514542425155414d4947474d5173774351" + + "59440a5651514745774a56557a454c4d416b4741" + + "31554543417743546c6b784554415042674e5642" + + "41634d43454a796232397262486c754d53457748" + + "7759445651514b0a4442684e655342445a584a30" + + "61575a70593246305a5342426458526f62334a70" + + "64486b784554415042674e5642414d4d43473135" + + "5932457562334a6e4d5345770a4877594a4b6f5a" + + "496876634e41516b4246684a71646e4e6f595768" + + "705a45426e625746706243356a62323077486863" + + "4e4d544d774e5449324d6a45774e5441780a5768" + + "634e4d6a4d774e5449304d6a45774e544178576a" + + "4342686a454c4d416b474131554542684d435656" + + "4d78437a414a42674e564241674d416b355a4d52" + + "45770a447759445651514844416843636d397661" + + "327835626a45684d423847413155454367775954" + + "586b675132567964476c6d61574e686447556751" + + "585630614739790a615852354d52457744775944" + + "565151444441687465574e684c6d39795a7a4568" + + "4d42384743537147534962334451454a41525953" + + "616e5a7a6147466f615752410a5a323168615777" + + "75593239744d494942496a414e42676b71686b69" + + "47397730424151454641414f43415138414d4949" + + "4243674b434151454138507574674634330a3032" + + "33754c737938444e645753315a467a5369324975" + + "6e69443947484b69664f6434317544672f375a75" + + "4731447071324259367a34635633686c74473067" + + "75530a4178754a4442735144706d503468666f77" + + "6a4141523962382b5138376454534e5462435a74" + + "3642746f4c6174326764654f4334433544427472" + + "684e79314d6a4f0a46416575493479506e6f7867" + + "31676135377741597742306c48746f2b4c383872" + + "566f53654d4348484b665944696954354e4b786c" + + "6e59413279447356454c31520a3662774334656d" + + "7a5770715a5152736e6f4531516e69642f6f5830" + + "4a6837324b796c2b7870516934424e5253696172" + + "67665549754c7858755a6c635045786c460a7145" + + "74646757624d456a65555876303845494652502f" + + "6f503361474a41366c346b665537383779737670" + + "4d774c72374b663062544b4c524f6b5874625132" + + "79760a6d31787162567262655635716177494441" + + "5141426f314177546a416442674e564851344546" + + "67515561783441714a2f3666514435344a30506b" + + "497951714b45330a61396f77487759445652306a" + + "42426777466f415561783441714a2f3666514435" + + "344a30506b497951714b453361396f7744415944" + + "5652305442415577417745420a2f7a414e42676b" + + "71686b6947397730424151554641414f43415145" + + "417a57397a5456594c387934633467494d464c73" + + "76335478442f742b554c616d4a675648340a5836" + + "65674536724d73426a69567a344e4b5a506f6c64" + + "556255394a52387233316e6e73695a574a637845" + + "7764364f6e443143766e654d7351382f34476739" + + "77360a486d495177455a33787032667135596c58" + + "50736d775255667054507554356f55616853586b" + + "7975564339796f31326b753841454f2f55375132" + + "616a6c5a6437370a79736f63582f6c546f49666e" + + "4d3573767a2b51542f4f7836624c435145357532" + + "78515032446c376935436242666c502b61615048" + + "324935756c444b67337371320a7a4e5942315868" + + "414b474f623773384a4f7a554538425143396f41" + + "4f6b4c4b55306955577548703268345366684d57" + + "76776d316f656f5363786f706a594964710a4a63" + + "476865412b3636462f687571796b6239304a5078" + + "4c4c48665050534e66544a75696377314f7a7574" + + "77796d5a695731673d3d0a2d2d2d2d2d454e4420" + + "43455254494649434154452d2d2d2d2d0a", +) + +// Script of interaction with openssl implementation: +// +// openssl s_server -cipher ECDHE-ECDSA-AES128-SHA \ +// -key server.key -cert server.crt -port 10443 +// +// The values for this test are obtained by building and running in client mode: +// % go test -test.run "TestRunClient" -connect -ciphersuites=0xc009 +// The recorded bytes are written to stdout. +// +// The server private key is: +// +// -----BEGIN EC PARAMETERS----- +// BgUrgQQAIw== +// -----END EC PARAMETERS----- +// -----BEGIN EC PRIVATE KEY----- +// MIHcAgEBBEIBmIPpCa0Kyeo9M/nq5mHxeFIGlw+MqakWcvHu3Keo7xK9ZWG7JG3a +// XfS01efjqSZJvF2DoL+Sly4A5iBn0Me9mdegBwYFK4EEACOhgYkDgYYABADEoe2+ +// mPkLSHM2fsMWVhEi8j1TwztNIT3Na3Xm9rDcmt8mwbyyh/ByMnyzZC8ckLzqaCMQ +// fv7jJcBIOmngKG3TNwDvBGLdDaCccGKD2IHTZDGqnpcxvZawaMCbI952ZD8aXH/p +// Eg5YWLZfcN2b2OrV1/XVzLm2nzBmW2aaIOIn5b/+Ow== +// -----END EC PRIVATE KEY----- +// +// and certificate is: +// +// -----BEGIN CERTIFICATE----- +// MIICADCCAWICCQC4vy1HoNLr9DAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw +// EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +// eSBMdGQwHhcNMTIxMTIyMTUwNjMyWhcNMjIxMTIwMTUwNjMyWjBFMQswCQYDVQQG +// EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk +// Z2l0cyBQdHkgTHRkMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAxKHtvpj5C0hz +// Nn7DFlYRIvI9U8M7TSE9zWt15vaw3JrfJsG8sofwcjJ8s2QvHJC86mgjEH7+4yXA +// SDpp4Cht0zcA7wRi3Q2gnHBig9iB02Qxqp6XMb2WsGjAmyPedmQ/Glx/6RIOWFi2 +// X3Ddm9jq1df11cy5tp8wZltmmiDiJ+W//jswCQYHKoZIzj0EAQOBjAAwgYgCQgGI +// ok/r4kXFSH0brPXtmJ2uR3DAXhu2L73xtk23YUDTEaLO7gt+kn7/dp3DO36lP876 +// EOJZ7EctfKzaTpcOFaBv0AJCAU38vmcTnC0FDr0/o4wlwTMTgw2UBrvUN3r27HrJ +// hi7d1xFpf4V8Vt77MXgr5Md4Da7Lvp5ONiQxe2oPOZUSB48q +// -----END CERTIFICATE----- +var ecdheECDSAAESClientScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00, + 0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xc0, 0x09, + 0x01, 0x00, 0x00, 0x1b, 0x00, 0x05, 0x00, 0x05, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x08, 0x00, 0x06, 0x00, 0x17, 0x00, 0x18, 0x00, + 0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x54, 0x02, 0x00, 0x00, + 0x50, 0x03, 0x01, 0x50, 0xd7, 0x19, 0xc9, 0x03, + 0xc2, 0x3a, 0xc6, 0x1f, 0x0a, 0x84, 0x9e, 0xd7, + 0xf4, 0x7e, 0x07, 0x6d, 0xa8, 0xe4, 0xa9, 0x4f, + 0x22, 0x50, 0xa2, 0x19, 0x24, 0x44, 0x42, 0x65, + 0xaa, 0xba, 0x3a, 0x20, 0x90, 0x70, 0xb7, 0xe5, + 0x57, 0xed, 0xb1, 0xb1, 0x43, 0x4b, 0xa1, 0x4e, + 0xee, 0x7a, 0x5b, 0x88, 0xf6, 0xa6, 0x73, 0x3b, + 0xcb, 0xa7, 0xbd, 0x57, 0x50, 0xf2, 0x72, 0x8c, + 0xbc, 0x45, 0x73, 0xaa, 0xc0, 0x09, 0x00, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, + 0x02, 0x16, 0x03, 0x01, 0x02, 0x0e, 0x0b, 0x00, + 0x02, 0x0a, 0x00, 0x02, 0x07, 0x00, 0x02, 0x04, + 0x30, 0x82, 0x02, 0x00, 0x30, 0x82, 0x01, 0x62, + 0x02, 0x09, 0x00, 0xb8, 0xbf, 0x2d, 0x47, 0xa0, + 0xd2, 0xeb, 0xf4, 0x30, 0x09, 0x06, 0x07, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30, 0x45, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, + 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, + 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, + 0x0d, 0x31, 0x32, 0x31, 0x31, 0x32, 0x32, 0x31, + 0x35, 0x30, 0x36, 0x33, 0x32, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x31, 0x31, 0x32, 0x30, 0x31, 0x35, + 0x30, 0x36, 0x33, 0x32, 0x5a, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9b, 0x30, + 0x10, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, + 0x23, 0x03, 0x81, 0x86, 0x00, 0x04, 0x00, 0xc4, + 0xa1, 0xed, 0xbe, 0x98, 0xf9, 0x0b, 0x48, 0x73, + 0x36, 0x7e, 0xc3, 0x16, 0x56, 0x11, 0x22, 0xf2, + 0x3d, 0x53, 0xc3, 0x3b, 0x4d, 0x21, 0x3d, 0xcd, + 0x6b, 0x75, 0xe6, 0xf6, 0xb0, 0xdc, 0x9a, 0xdf, + 0x26, 0xc1, 0xbc, 0xb2, 0x87, 0xf0, 0x72, 0x32, + 0x7c, 0xb3, 0x64, 0x2f, 0x1c, 0x90, 0xbc, 0xea, + 0x68, 0x23, 0x10, 0x7e, 0xfe, 0xe3, 0x25, 0xc0, + 0x48, 0x3a, 0x69, 0xe0, 0x28, 0x6d, 0xd3, 0x37, + 0x00, 0xef, 0x04, 0x62, 0xdd, 0x0d, 0xa0, 0x9c, + 0x70, 0x62, 0x83, 0xd8, 0x81, 0xd3, 0x64, 0x31, + 0xaa, 0x9e, 0x97, 0x31, 0xbd, 0x96, 0xb0, 0x68, + 0xc0, 0x9b, 0x23, 0xde, 0x76, 0x64, 0x3f, 0x1a, + 0x5c, 0x7f, 0xe9, 0x12, 0x0e, 0x58, 0x58, 0xb6, + 0x5f, 0x70, 0xdd, 0x9b, 0xd8, 0xea, 0xd5, 0xd7, + 0xf5, 0xd5, 0xcc, 0xb9, 0xb6, 0x9f, 0x30, 0x66, + 0x5b, 0x66, 0x9a, 0x20, 0xe2, 0x27, 0xe5, 0xbf, + 0xfe, 0x3b, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x01, 0x03, 0x81, 0x8c, + 0x00, 0x30, 0x81, 0x88, 0x02, 0x42, 0x01, 0x88, + 0xa2, 0x4f, 0xeb, 0xe2, 0x45, 0xc5, 0x48, 0x7d, + 0x1b, 0xac, 0xf5, 0xed, 0x98, 0x9d, 0xae, 0x47, + 0x70, 0xc0, 0x5e, 0x1b, 0xb6, 0x2f, 0xbd, 0xf1, + 0xb6, 0x4d, 0xb7, 0x61, 0x40, 0xd3, 0x11, 0xa2, + 0xce, 0xee, 0x0b, 0x7e, 0x92, 0x7e, 0xff, 0x76, + 0x9d, 0xc3, 0x3b, 0x7e, 0xa5, 0x3f, 0xce, 0xfa, + 0x10, 0xe2, 0x59, 0xec, 0x47, 0x2d, 0x7c, 0xac, + 0xda, 0x4e, 0x97, 0x0e, 0x15, 0xa0, 0x6f, 0xd0, + 0x02, 0x42, 0x01, 0x4d, 0xfc, 0xbe, 0x67, 0x13, + 0x9c, 0x2d, 0x05, 0x0e, 0xbd, 0x3f, 0xa3, 0x8c, + 0x25, 0xc1, 0x33, 0x13, 0x83, 0x0d, 0x94, 0x06, + 0xbb, 0xd4, 0x37, 0x7a, 0xf6, 0xec, 0x7a, 0xc9, + 0x86, 0x2e, 0xdd, 0xd7, 0x11, 0x69, 0x7f, 0x85, + 0x7c, 0x56, 0xde, 0xfb, 0x31, 0x78, 0x2b, 0xe4, + 0xc7, 0x78, 0x0d, 0xae, 0xcb, 0xbe, 0x9e, 0x4e, + 0x36, 0x24, 0x31, 0x7b, 0x6a, 0x0f, 0x39, 0x95, + 0x12, 0x07, 0x8f, 0x2a, 0x16, 0x03, 0x01, 0x00, + 0xd6, 0x0c, 0x00, 0x00, 0xd2, 0x03, 0x00, 0x17, + 0x41, 0x04, 0x33, 0xed, 0xe1, 0x10, 0x3d, 0xe2, + 0xb0, 0x81, 0x5e, 0x01, 0x1b, 0x00, 0x4a, 0x7d, + 0xdc, 0xc5, 0x78, 0x02, 0xb1, 0x9a, 0x78, 0x92, + 0x34, 0xd9, 0x23, 0xcc, 0x01, 0xfb, 0x0c, 0x49, + 0x1c, 0x4a, 0x59, 0x8a, 0x80, 0x1b, 0x34, 0xf0, + 0xe8, 0x87, 0x1b, 0x7c, 0xfb, 0x72, 0xf5, 0xea, + 0xf9, 0xf3, 0xff, 0xa6, 0x3e, 0x4e, 0xac, 0xbc, + 0xee, 0x14, 0x2b, 0x87, 0xd4, 0x0b, 0xda, 0x19, + 0x60, 0x2b, 0x00, 0x8b, 0x30, 0x81, 0x88, 0x02, + 0x42, 0x01, 0x75, 0x46, 0x4f, 0x97, 0x9f, 0xc5, + 0xf9, 0x4c, 0x38, 0xcf, 0x3b, 0x37, 0x1a, 0x6b, + 0x53, 0xfc, 0x05, 0x73, 0x7d, 0x98, 0x2c, 0x5b, + 0x76, 0xd4, 0x37, 0x1f, 0x50, 0x6d, 0xad, 0xc6, + 0x0f, 0x8f, 0x7b, 0xcc, 0x60, 0x8e, 0x04, 0x00, + 0x21, 0x80, 0xa8, 0xa5, 0x98, 0xf2, 0x42, 0xf2, + 0xc3, 0xf6, 0x44, 0x50, 0xc4, 0x7a, 0xae, 0x6f, + 0x74, 0xa0, 0x7f, 0x07, 0x7a, 0x0b, 0xbb, 0x41, + 0x9e, 0x3c, 0x0b, 0x02, 0x42, 0x01, 0xbe, 0x64, + 0xaa, 0x12, 0x03, 0xfb, 0xd8, 0x4f, 0x93, 0xf9, + 0x92, 0x54, 0x0d, 0x9c, 0x9d, 0x53, 0x88, 0x19, + 0x69, 0x94, 0xfc, 0xd6, 0xf7, 0x60, 0xcf, 0x70, + 0x64, 0x15, 0x1b, 0x02, 0x22, 0x56, 0xb0, 0x2c, + 0xb1, 0x72, 0x4c, 0x9e, 0x7b, 0xf0, 0x53, 0x97, + 0x43, 0xac, 0x11, 0x62, 0xe5, 0x5a, 0xf1, 0x7e, + 0x87, 0x8f, 0x5c, 0x43, 0x1d, 0xae, 0x56, 0x28, + 0xdb, 0x76, 0x15, 0xd8, 0x1c, 0x73, 0xce, 0x16, + 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00, + 0x42, 0x41, 0x04, 0x1e, 0x18, 0x37, 0xef, 0x0d, + 0x19, 0x51, 0x88, 0x35, 0x75, 0x71, 0xb5, 0xe5, + 0x54, 0x5b, 0x12, 0x2e, 0x8f, 0x09, 0x67, 0xfd, + 0xa7, 0x24, 0x20, 0x3e, 0xb2, 0x56, 0x1c, 0xce, + 0x97, 0x28, 0x5e, 0xf8, 0x2b, 0x2d, 0x4f, 0x9e, + 0xf1, 0x07, 0x9f, 0x6c, 0x4b, 0x5b, 0x83, 0x56, + 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49, + 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b, + 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x1a, 0x45, + 0x92, 0x3b, 0xac, 0x8d, 0x91, 0x89, 0xd3, 0x2c, + 0xf4, 0x3c, 0x5f, 0x70, 0xf1, 0x79, 0xa5, 0x6a, + 0xcf, 0x97, 0x8f, 0x3f, 0x73, 0x08, 0xca, 0x3f, + 0x55, 0xb0, 0x28, 0xd1, 0x6f, 0xcd, 0x9b, 0xca, + 0xb6, 0xb7, 0xd0, 0xa5, 0x21, 0x5b, 0x08, 0xf8, + 0x42, 0xe2, 0xdf, 0x25, 0x6a, 0x16, + }, + { + 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, + 0x01, 0x00, 0x30, 0x30, 0x83, 0xb6, 0x51, 0x8a, + 0x85, 0x4a, 0xee, 0xe4, 0xb6, 0xae, 0xf3, 0xc1, + 0xdc, 0xd2, 0x04, 0xb3, 0xd0, 0x25, 0x47, 0x5f, + 0xac, 0x83, 0xa3, 0x7d, 0xcf, 0x47, 0x92, 0xed, + 0x92, 0x6c, 0xd1, 0x6e, 0xfd, 0x63, 0xf5, 0x2d, + 0x89, 0xd8, 0x04, 0x8c, 0x62, 0x71, 0xae, 0x5e, + 0x32, 0x48, 0xf8, + }, + { + 0x17, 0x03, 0x01, 0x00, 0x20, 0xcf, 0x5e, 0xba, + 0xf4, 0x47, 0x32, 0x35, 0x9b, 0x85, 0xdc, 0xb3, + 0xff, 0x77, 0x90, 0xd9, 0x2b, 0xbd, 0x59, 0x2a, + 0x33, 0xe4, 0x6e, 0x9b, 0xfc, 0x1c, 0x73, 0x3f, + 0x5e, 0x1e, 0xe3, 0xa4, 0xc2, 0x17, 0x03, 0x01, + 0x00, 0x20, 0x05, 0xdf, 0x2d, 0x9b, 0x29, 0x7f, + 0x97, 0xcd, 0x49, 0x04, 0x53, 0x22, 0x1a, 0xa1, + 0xa1, 0xe6, 0x38, 0x3a, 0x56, 0x37, 0x1f, 0xd8, + 0x3a, 0x12, 0x2c, 0xf0, 0xeb, 0x61, 0x35, 0x76, + 0xe5, 0xf0, 0x15, 0x03, 0x01, 0x00, 0x20, 0xa5, + 0x56, 0xb5, 0x49, 0x4b, 0xc2, 0xd4, 0x4c, 0xf6, + 0x95, 0x15, 0x7d, 0x41, 0x1d, 0x5c, 0x00, 0x0e, + 0x20, 0xb1, 0x0a, 0xbc, 0xc9, 0x2a, 0x09, 0x17, + 0xb4, 0xaa, 0x1c, 0x79, 0xda, 0x79, 0x27, + }, +} diff --git a/libgo/go/crypto/tls/handshake_messages.go b/libgo/go/crypto/tls/handshake_messages.go index cdd4917..8395200 100644 --- a/libgo/go/crypto/tls/handshake_messages.go +++ b/libgo/go/crypto/tls/handshake_messages.go @@ -20,6 +20,7 @@ type clientHelloMsg struct { supportedPoints []uint8 ticketSupported bool sessionTicket []uint8 + signatureAndHashes []signatureAndHash } func (m *clientHelloMsg) equal(i interface{}) bool { @@ -40,7 +41,8 @@ func (m *clientHelloMsg) equal(i interface{}) bool { eqUint16s(m.supportedCurves, m1.supportedCurves) && bytes.Equal(m.supportedPoints, m1.supportedPoints) && m.ticketSupported == m1.ticketSupported && - bytes.Equal(m.sessionTicket, m1.sessionTicket) + bytes.Equal(m.sessionTicket, m1.sessionTicket) && + eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) } func (m *clientHelloMsg) marshal() []byte { @@ -74,6 +76,10 @@ func (m *clientHelloMsg) marshal() []byte { extensionsLength += len(m.sessionTicket) numExtensions++ } + if len(m.signatureAndHashes) > 0 { + extensionsLength += 2 + 2*len(m.signatureAndHashes) + numExtensions++ + } if numExtensions > 0 { extensionsLength += 4 * numExtensions length += 2 + extensionsLength @@ -199,6 +205,25 @@ func (m *clientHelloMsg) marshal() []byte { copy(z, m.sessionTicket) z = z[len(m.sessionTicket):] } + if len(m.signatureAndHashes) > 0 { + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + z[0] = byte(extensionSignatureAlgorithms >> 8) + z[1] = byte(extensionSignatureAlgorithms) + l := 2 + 2*len(m.signatureAndHashes) + z[2] = byte(l >> 8) + z[3] = byte(l) + z = z[4:] + + l -= 2 + z[0] = byte(l >> 8) + z[1] = byte(l) + z = z[2:] + for _, sigAndHash := range m.signatureAndHashes { + z[0] = sigAndHash.hash + z[1] = sigAndHash.signature + z = z[2:] + } + } m.raw = x @@ -249,6 +274,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { m.ocspStapling = false m.ticketSupported = false m.sessionTicket = nil + m.signatureAndHashes = nil if len(data) == 0 { // ClientHello is optionally followed by extension data @@ -336,6 +362,23 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { // http://tools.ietf.org/html/rfc5077#section-3.2 m.ticketSupported = true m.sessionTicket = data[:length] + case extensionSignatureAlgorithms: + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + if length < 2 || length&1 != 0 { + return false + } + l := int(data[0])<<8 | int(data[1]) + if l != length-2 { + return false + } + n := l / 2 + d := data[2:] + m.signatureAndHashes = make([]signatureAndHash, n) + for i := range m.signatureAndHashes { + m.signatureAndHashes[i].hash = d[0] + m.signatureAndHashes[i].signature = d[1] + d = d[2:] + } } data = data[length:] } @@ -899,8 +942,14 @@ func (m *nextProtoMsg) unmarshal(data []byte) bool { } type certificateRequestMsg struct { - raw []byte + raw []byte + // hasSignatureAndHash indicates whether this message includes a list + // of signature and hash functions. This change was introduced with TLS + // 1.2. + hasSignatureAndHash bool + certificateTypes []byte + signatureAndHashes []signatureAndHash certificateAuthorities [][]byte } @@ -912,7 +961,8 @@ func (m *certificateRequestMsg) equal(i interface{}) bool { return bytes.Equal(m.raw, m1.raw) && bytes.Equal(m.certificateTypes, m1.certificateTypes) && - eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) + eqByteSlices(m.certificateAuthorities, m1.certificateAuthorities) && + eqSignatureAndHashes(m.signatureAndHashes, m1.signatureAndHashes) } func (m *certificateRequestMsg) marshal() (x []byte) { @@ -928,6 +978,10 @@ func (m *certificateRequestMsg) marshal() (x []byte) { } length += casLength + if m.hasSignatureAndHash { + length += 2 + 2*len(m.signatureAndHashes) + } + x = make([]byte, 4+length) x[0] = typeCertificateRequest x[1] = uint8(length >> 16) @@ -938,6 +992,19 @@ func (m *certificateRequestMsg) marshal() (x []byte) { copy(x[5:], m.certificateTypes) y := x[5+len(m.certificateTypes):] + + if m.hasSignatureAndHash { + n := len(m.signatureAndHashes) * 2 + y[0] = uint8(n >> 8) + y[1] = uint8(n) + y = y[2:] + for _, sigAndHash := range m.signatureAndHashes { + y[0] = sigAndHash.hash + y[1] = sigAndHash.signature + y = y[2:] + } + } + y[0] = uint8(casLength >> 8) y[1] = uint8(casLength) y = y[2:] @@ -978,6 +1045,27 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool { data = data[numCertTypes:] + if m.hasSignatureAndHash { + if len(data) < 2 { + return false + } + sigAndHashLen := uint16(data[0])<<8 | uint16(data[1]) + data = data[2:] + if sigAndHashLen&1 != 0 { + return false + } + if len(data) < int(sigAndHashLen) { + return false + } + numSigAndHash := sigAndHashLen / 2 + m.signatureAndHashes = make([]signatureAndHash, numSigAndHash) + for i := range m.signatureAndHashes { + m.signatureAndHashes[i].hash = data[0] + m.signatureAndHashes[i].signature = data[1] + data = data[2:] + } + } + if len(data) < 2 { return false } @@ -1013,8 +1101,10 @@ func (m *certificateRequestMsg) unmarshal(data []byte) bool { } type certificateVerifyMsg struct { - raw []byte - signature []byte + raw []byte + hasSignatureAndHash bool + signatureAndHash signatureAndHash + signature []byte } func (m *certificateVerifyMsg) equal(i interface{}) bool { @@ -1024,6 +1114,9 @@ func (m *certificateVerifyMsg) equal(i interface{}) bool { } return bytes.Equal(m.raw, m1.raw) && + m.hasSignatureAndHash == m1.hasSignatureAndHash && + m.signatureAndHash.hash == m1.signatureAndHash.hash && + m.signatureAndHash.signature == m1.signatureAndHash.signature && bytes.Equal(m.signature, m1.signature) } @@ -1035,14 +1128,23 @@ func (m *certificateVerifyMsg) marshal() (x []byte) { // See http://tools.ietf.org/html/rfc4346#section-7.4.8 siglength := len(m.signature) length := 2 + siglength + if m.hasSignatureAndHash { + length += 2 + } x = make([]byte, 4+length) x[0] = typeCertificateVerify x[1] = uint8(length >> 16) x[2] = uint8(length >> 8) x[3] = uint8(length) - x[4] = uint8(siglength >> 8) - x[5] = uint8(siglength) - copy(x[6:], m.signature) + y := x[4:] + if m.hasSignatureAndHash { + y[0] = m.signatureAndHash.hash + y[1] = m.signatureAndHash.signature + y = y[2:] + } + y[0] = uint8(siglength >> 8) + y[1] = uint8(siglength) + copy(y[2:], m.signature) m.raw = x @@ -1061,12 +1163,23 @@ func (m *certificateVerifyMsg) unmarshal(data []byte) bool { return false } - siglength := int(data[4])<<8 + int(data[5]) - if len(data)-6 != siglength { + data = data[4:] + if m.hasSignatureAndHash { + m.signatureAndHash.hash = data[0] + m.signatureAndHash.signature = data[1] + data = data[2:] + } + + if len(data) < 2 { + return false + } + siglength := int(data[0])<<8 + int(data[1]) + data = data[2:] + if len(data) != siglength { return false } - m.signature = data[6:] + m.signature = data return true } @@ -1165,3 +1278,16 @@ func eqByteSlices(x, y [][]byte) bool { } return true } + +func eqSignatureAndHashes(x, y []signatureAndHash) bool { + if len(x) != len(y) { + return false + } + for i, v := range x { + v2 := y[i] + if v.hash != v2.hash || v.signature != v2.signature { + return false + } + } + return true +} diff --git a/libgo/go/crypto/tls/handshake_messages_test.go b/libgo/go/crypto/tls/handshake_messages_test.go index 3434bad..4f569ee 100644 --- a/libgo/go/crypto/tls/handshake_messages_test.go +++ b/libgo/go/crypto/tls/handshake_messages_test.go @@ -135,6 +135,9 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { m.sessionTicket = randomBytes(rand.Intn(300), rand) } } + if rand.Intn(10) > 5 { + m.signatureAndHashes = supportedSKXSignatureAlgorithms + } return reflect.ValueOf(m) } diff --git a/libgo/go/crypto/tls/handshake_server.go b/libgo/go/crypto/tls/handshake_server.go index 823730c..c9ccf67 100644 --- a/libgo/go/crypto/tls/handshake_server.go +++ b/libgo/go/crypto/tls/handshake_server.go @@ -6,9 +6,11 @@ package tls import ( "crypto" + "crypto/ecdsa" "crypto/rsa" "crypto/subtle" "crypto/x509" + "encoding/asn1" "errors" "io" ) @@ -21,10 +23,12 @@ type serverHandshakeState struct { hello *serverHelloMsg suite *cipherSuite ellipticOk bool + ecdsaOk bool sessionState *sessionState finishedHash finishedHash masterSecret []byte certsFromClient [][]byte + cert *Certificate } // serverHandshake performs a TLS handshake as a server. @@ -98,7 +102,7 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) { if !ok { return false, c.sendAlert(alertUnexpectedMessage) } - c.vers, ok = mutualVersion(hs.clientHello.vers) + c.vers, ok = config.mutualVersion(hs.clientHello.vers) if !ok { return false, c.sendAlert(alertProtocolVersion) } @@ -156,11 +160,25 @@ Curves: if len(hs.clientHello.serverName) > 0 { c.serverName = hs.clientHello.serverName } - if hs.clientHello.nextProtoNeg { + // Although sending an empty NPN extension is reasonable, Firefox has + // had a bug around this. Best to send nothing at all if + // config.NextProtos is empty. See + // https://code.google.com/p/go/issues/detail?id=5445. + if hs.clientHello.nextProtoNeg && len(config.NextProtos) > 0 { hs.hello.nextProtoNeg = true hs.hello.nextProtos = config.NextProtos } + if len(config.Certificates) == 0 { + return false, c.sendAlert(alertInternalError) + } + hs.cert = &config.Certificates[0] + if len(hs.clientHello.serverName) > 0 { + hs.cert = config.getCertificateForName(hs.clientHello.serverName) + } + + _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey) + if hs.checkForResumption() { return true, nil } @@ -175,7 +193,7 @@ Curves: } for _, id := range preferenceList { - if hs.suite = c.tryCipherSuite(id, supportedList, hs.ellipticOk); hs.suite != nil { + if hs.suite = c.tryCipherSuite(id, supportedList, c.vers, hs.ellipticOk, hs.ecdsaOk); hs.suite != nil { break } } @@ -199,7 +217,7 @@ func (hs *serverHandshakeState) checkForResumption() bool { if hs.sessionState.vers > hs.clientHello.vers { return false } - if vers, ok := mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers { + if vers, ok := c.config.mutualVersion(hs.sessionState.vers); !ok || vers != hs.sessionState.vers { return false } @@ -216,7 +234,7 @@ func (hs *serverHandshakeState) checkForResumption() bool { } // Check that we also support the ciphersuite from the session. - hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.ellipticOk) + hs.suite = c.tryCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers, hs.ellipticOk, hs.ecdsaOk) if hs.suite == nil { return false } @@ -258,15 +276,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { config := hs.c.config c := hs.c - if len(config.Certificates) == 0 { - return c.sendAlert(alertInternalError) - } - cert := &config.Certificates[0] - if len(hs.clientHello.serverName) > 0 { - cert = config.getCertificateForName(hs.clientHello.serverName) - } - - if hs.clientHello.ocspStapling && len(cert.OCSPStaple) > 0 { + if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 { hs.hello.ocspStapling = true } @@ -276,20 +286,20 @@ func (hs *serverHandshakeState) doFullHandshake() error { c.writeRecord(recordTypeHandshake, hs.hello.marshal()) certMsg := new(certificateMsg) - certMsg.certificates = cert.Certificate + certMsg.certificates = hs.cert.Certificate hs.finishedHash.Write(certMsg.marshal()) c.writeRecord(recordTypeHandshake, certMsg.marshal()) if hs.hello.ocspStapling { certStatus := new(certificateStatusMsg) certStatus.statusType = statusTypeOCSP - certStatus.response = cert.OCSPStaple + certStatus.response = hs.cert.OCSPStaple hs.finishedHash.Write(certStatus.marshal()) c.writeRecord(recordTypeHandshake, certStatus.marshal()) } - keyAgreement := hs.suite.ka() - skx, err := keyAgreement.generateServerKeyExchange(config, cert, hs.clientHello, hs.hello) + keyAgreement := hs.suite.ka(c.vers) + skx, err := keyAgreement.generateServerKeyExchange(config, hs.cert, hs.clientHello, hs.hello) if err != nil { c.sendAlert(alertHandshakeFailure) return err @@ -302,7 +312,14 @@ func (hs *serverHandshakeState) doFullHandshake() error { if config.ClientAuth >= RequestClientCert { // Request a client certificate certReq := new(certificateRequestMsg) - certReq.certificateTypes = []byte{certTypeRSASign} + certReq.certificateTypes = []byte{ + byte(certTypeRSASign), + byte(certTypeECDSASign), + } + if c.vers >= VersionTLS12 { + certReq.hasSignatureAndHash = true + certReq.signatureAndHashes = supportedClientCertSignatureAlgorithms + } // An empty list of certificateAuthorities signals to // the client that it may send any certificate in response @@ -320,7 +337,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { hs.finishedHash.Write(helloDone.marshal()) c.writeRecord(recordTypeHandshake, helloDone.marshal()) - var pub *rsa.PublicKey // public key for client auth, if any + var pub crypto.PublicKey // public key for client auth, if any msg, err := c.readHandshake() if err != nil { @@ -365,7 +382,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { // If we received a client cert in response to our certificate request message, // the client will send us a certificateVerifyMsg immediately after the - // clientKeyExchangeMsg. This message is a MD5SHA1 digest of all preceding + // clientKeyExchangeMsg. This message is a digest of all preceding // handshake-layer messages that is signed using the private key corresponding // to the client's certificate. This allows us to verify that the client is in // possession of the private key of the certificate. @@ -379,10 +396,25 @@ func (hs *serverHandshakeState) doFullHandshake() error { return c.sendAlert(alertUnexpectedMessage) } - digest := make([]byte, 0, 36) - digest = hs.finishedHash.serverMD5.Sum(digest) - digest = hs.finishedHash.serverSHA1.Sum(digest) - err = rsa.VerifyPKCS1v15(pub, crypto.MD5SHA1, digest, certVerify.signature) + switch key := pub.(type) { + case *ecdsa.PublicKey: + ecdsaSig := new(ecdsaSignature) + if _, err = asn1.Unmarshal(certVerify.signature, ecdsaSig); err != nil { + break + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + err = errors.New("ECDSA signature contained zero or negative values") + break + } + digest, _, _ := hs.finishedHash.hashForClientCertificate(signatureECDSA) + if !ecdsa.Verify(key, digest, ecdsaSig.R, ecdsaSig.S) { + err = errors.New("ECDSA verification failure") + break + } + case *rsa.PublicKey: + digest, hashFunc, _ := hs.finishedHash.hashForClientCertificate(signatureRSA) + err = rsa.VerifyPKCS1v15(key, hashFunc, digest, certVerify.signature) + } if err != nil { c.sendAlert(alertBadCertificate) return errors.New("could not validate signature of connection nonces: " + err.Error()) @@ -391,7 +423,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { hs.finishedHash.Write(certVerify.marshal()) } - preMasterSecret, err := keyAgreement.processClientKeyExchange(config, cert, ckx, c.vers) + preMasterSecret, err := keyAgreement.processClientKeyExchange(config, hs.cert, ckx, c.vers) if err != nil { c.sendAlert(alertHandshakeFailure) return err @@ -407,12 +439,20 @@ func (hs *serverHandshakeState) establishKeys() error { clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromMasterSecret(c.vers, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen) - clientCipher := hs.suite.cipher(clientKey, clientIV, true /* for reading */) - clientHash := hs.suite.mac(c.vers, clientMAC) - c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) + var clientCipher, serverCipher interface{} + var clientHash, serverHash macFunction - serverCipher := hs.suite.cipher(serverKey, serverIV, false /* not for reading */) - serverHash := hs.suite.mac(c.vers, serverMAC) + if hs.suite.aead == nil { + clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */) + clientHash = hs.suite.mac(c.vers, clientMAC) + serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */) + serverHash = hs.suite.mac(c.vers, serverMAC) + } else { + clientCipher = hs.suite.aead(clientKey, clientIV) + serverCipher = hs.suite.aead(serverKey, serverIV) + } + + c.in.prepareCipherSpec(c.vers, clientCipher, clientHash) c.out.prepareCipherSpec(c.vers, serverCipher, serverHash) return nil @@ -502,7 +542,7 @@ func (hs *serverHandshakeState) sendFinished() error { // processCertsFromClient takes a chain of client certificates either from a // Certificates message or from a sessionState and verifies them. It returns // the public key of the leaf certificate. -func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (*rsa.PublicKey, error) { +func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (crypto.PublicKey, error) { c := hs.c hs.certsFromClient = certificates @@ -549,8 +589,11 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (* } if len(certs) > 0 { - pub, ok := certs[0].PublicKey.(*rsa.PublicKey) - if !ok { + var pub crypto.PublicKey + switch key := certs[0].PublicKey.(type) { + case *ecdsa.PublicKey, *rsa.PublicKey: + pub = key + default: return nil, c.sendAlert(alertUnsupportedCertificate) } c.peerCertificates = certs @@ -562,7 +605,7 @@ func (hs *serverHandshakeState) processCertsFromClient(certificates [][]byte) (* // tryCipherSuite returns a cipherSuite with the given id if that cipher suite // is acceptable to use. -func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipticOk bool) *cipherSuite { +func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16, ellipticOk, ecdsaOk bool) *cipherSuite { for _, supported := range supportedCipherSuites { if id == supported { var candidate *cipherSuite @@ -578,7 +621,13 @@ func (c *Conn) tryCipherSuite(id uint16, supportedCipherSuites []uint16, ellipti } // Don't select a ciphersuite which we can't // support for this client. - if candidate.elliptic && !ellipticOk { + if (candidate.flags&suiteECDHE != 0) && !ellipticOk { + continue + } + if (candidate.flags&suiteECDSA != 0) != ecdsaOk { + continue + } + if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 { continue } return candidate diff --git a/libgo/go/crypto/tls/handshake_server_test.go b/libgo/go/crypto/tls/handshake_server_test.go index bf8cbe3..c08eba7 100644 --- a/libgo/go/crypto/tls/handshake_server_test.go +++ b/libgo/go/crypto/tls/handshake_server_test.go @@ -6,6 +6,8 @@ package tls import ( "bytes" + "crypto/ecdsa" + "crypto/elliptic" "crypto/rsa" "crypto/x509" "encoding/hex" @@ -41,13 +43,15 @@ func init() { testConfig.Time = func() time.Time { return time.Unix(0, 0) } testConfig.Rand = zeroSource{} testConfig.Certificates = make([]Certificate, 2) - testConfig.Certificates[0].Certificate = [][]byte{testCertificate} - testConfig.Certificates[0].PrivateKey = testPrivateKey + testConfig.Certificates[0].Certificate = [][]byte{testRSACertificate} + testConfig.Certificates[0].PrivateKey = testRSAPrivateKey testConfig.Certificates[1].Certificate = [][]byte{testSNICertificate} - testConfig.Certificates[1].PrivateKey = testPrivateKey + testConfig.Certificates[1].PrivateKey = testRSAPrivateKey testConfig.BuildNameToCertificate() testConfig.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA} testConfig.InsecureSkipVerify = true + testConfig.MinVersion = VersionSSL30 + testConfig.MaxVersion = VersionTLS10 } func testClientHelloFailure(t *testing.T, m handshakeMessage, expected error) { @@ -100,6 +104,53 @@ func TestNoCompressionOverlap(t *testing.T) { testClientHelloFailure(t, clientHello, alertHandshakeFailure) } +func TestTLS12OnlyCipherSuites(t *testing.T) { + // Test that a Server doesn't select a TLS 1.2-only cipher suite when + // the client negotiates TLS 1.1. + var zeros [32]byte + + clientHello := &clientHelloMsg{ + vers: VersionTLS11, + random: zeros[:], + cipherSuites: []uint16{ + // The Server, by default, will use the client's + // preference order. So the GCM cipher suite + // will be selected unless it's excluded because + // of the version in this ClientHello. + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_RC4_128_SHA, + }, + compressionMethods: []uint8{compressionNone}, + supportedCurves: []uint16{curveP256, curveP384, curveP521}, + supportedPoints: []uint8{pointFormatUncompressed}, + } + + c, s := net.Pipe() + var reply interface{} + var clientErr error + go func() { + cli := Client(c, testConfig) + cli.vers = clientHello.vers + cli.writeRecord(recordTypeHandshake, clientHello.marshal()) + reply, clientErr = cli.readHandshake() + c.Close() + }() + config := *testConfig + config.CipherSuites = clientHello.cipherSuites + Server(s, &config).Handshake() + s.Close() + if clientErr != nil { + t.Fatal(clientErr) + } + serverHello, ok := reply.(*serverHelloMsg) + if !ok { + t.Fatalf("didn't get ServerHello message in reply. Got %v\n", reply) + } + if s := serverHello.cipherSuite; s != TLS_RSA_WITH_RC4_128_SHA { + t.Fatalf("bad cipher suite from server: %x", s) + } +} + func TestAlertForwarding(t *testing.T) { c, s := net.Pipe() go func() { @@ -110,7 +161,7 @@ func TestAlertForwarding(t *testing.T) { err := Server(s, testConfig).Handshake() s.Close() if e, ok := err.(*net.OpError); !ok || e.Err != error(alertUnknownCA) { - t.Errorf("Got error: %s; expected: %s", err, alertUnknownCA) + t.Errorf("Got error: %s; expected: %s", err, error(alertUnknownCA)) } } @@ -145,6 +196,7 @@ 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}, Certificates: testConfig.Certificates, + MaxVersion: VersionTLS11, } clientConfig := &Config{ CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_RC4_128_SHA}, @@ -211,22 +263,33 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config * } } -func TestHandshakeServerRC4(t *testing.T) { - testServerScript(t, "RC4", rc4ServerScript, testConfig, nil) +func TestHandshakeServerRSARC4(t *testing.T) { + testServerScript(t, "RSA-RC4", rsaRC4ServerScript, testConfig, nil) } -func TestHandshakeServer3DES(t *testing.T) { +func TestHandshakeServerRSA3DES(t *testing.T) { des3Config := new(Config) *des3Config = *testConfig des3Config.CipherSuites = []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA} - testServerScript(t, "3DES", des3ServerScript, des3Config, nil) + testServerScript(t, "RSA-3DES", rsaDES3ServerScript, des3Config, nil) } -func TestHandshakeServerAES(t *testing.T) { +func TestHandshakeServerRSAAES(t *testing.T) { aesConfig := new(Config) *aesConfig = *testConfig aesConfig.CipherSuites = []uint16{TLS_RSA_WITH_AES_128_CBC_SHA} - testServerScript(t, "AES", aesServerScript, aesConfig, nil) + testServerScript(t, "RSA-AES", rsaAESServerScript, aesConfig, nil) +} + +func TestHandshakeServerECDHEECDSAAES(t *testing.T) { + ecdsaConfig := new(Config) + *ecdsaConfig = *testConfig + ecdsaConfig.Certificates = make([]Certificate, 1) + ecdsaConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} + ecdsaConfig.Certificates[0].PrivateKey = testECDSAPrivateKey + ecdsaConfig.BuildNameToCertificate() + ecdsaConfig.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA} + testServerScript(t, "ECDHE-ECDSA-AES", ecdheECDSAAESServerScript, ecdsaConfig, nil) } func TestHandshakeServerSSLv3(t *testing.T) { @@ -245,6 +308,15 @@ func TestResumption(t *testing.T) { testServerScript(t, "Resume", serverResumeTest, testConfig, nil) } +func TestTLS12ClientCertServer(t *testing.T) { + config := *testConfig + config.MaxVersion = VersionTLS12 + config.ClientAuth = RequireAnyClientCert + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} + + testServerScript(t, "TLS12", tls12ServerScript, &config, nil) +} + type clientauthTest struct { name string clientauth ClientAuthType @@ -252,16 +324,67 @@ type clientauthTest struct { script [][]byte } -func TestClientAuth(t *testing.T) { - for _, cat := range clientauthTests { +func TestClientAuthRSA(t *testing.T) { + for _, cat := range clientauthRSATests { + t.Log("running", cat.name) + cfg := new(Config) + *cfg = *testConfig + cfg.ClientAuth = cat.clientauth + testServerScript(t, cat.name, cat.script, cfg, cat.peers) + } +} + +func TestClientAuthECDSA(t *testing.T) { + for _, cat := range clientauthECDSATests { t.Log("running", cat.name) cfg := new(Config) *cfg = *testConfig + cfg.Certificates = make([]Certificate, 1) + cfg.Certificates[0].Certificate = [][]byte{testECDSACertificate} + cfg.Certificates[0].PrivateKey = testECDSAPrivateKey + cfg.BuildNameToCertificate() + cfg.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA} cfg.ClientAuth = cat.clientauth testServerScript(t, cat.name, cat.script, cfg, cat.peers) } } +// TestCipherSuiteCertPreferance ensures that we select an RSA ciphersuite with +// an RSA certificate and an ECDSA ciphersuite with an ECDSA certificate. +func TestCipherSuiteCertPreferance(t *testing.T) { + var config = *testConfig + config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA} + config.MaxVersion = VersionTLS11 + config.PreferServerCipherSuites = true + testServerScript(t, "CipherSuiteCertPreference", tls11ECDHEAESServerScript, &config, nil) + + config = *testConfig + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA} + config.Certificates = []Certificate{ + Certificate{ + Certificate: [][]byte{testECDSACertificate}, + PrivateKey: testECDSAPrivateKey, + }, + } + config.BuildNameToCertificate() + config.PreferServerCipherSuites = true + testServerScript(t, "CipherSuiteCertPreference2", ecdheECDSAAESServerScript, &config, nil) +} + +func TestTLS11Server(t *testing.T) { + var config = *testConfig + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA} + config.MaxVersion = VersionTLS11 + testServerScript(t, "TLS11", tls11ECDHEAESServerScript, &config, nil) +} + +func TestAESGCM(t *testing.T) { + var config = *testConfig + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} + config.MaxVersion = VersionTLS12 + testServerScript(t, "AES-GCM", aesGCMServerScript, &config, nil) +} + // recordingConn is a net.Conn that records the traffic that passes through it. // WriteTo can be used to produce Go code that contains the recorded traffic. type recordingConn struct { @@ -331,10 +454,28 @@ var serve = flag.Bool("serve", false, "run a TLS server on :10443") var testCipherSuites = flag.String("ciphersuites", "0x"+strconv.FormatInt(int64(TLS_RSA_WITH_RC4_128_SHA), 16), "cipher suites to accept in serving mode") +var testMinVersion = flag.String("minversion", + "0x"+strconv.FormatInt(int64(VersionSSL30), 16), + "minimum version to negotiate") +var testMaxVersion = flag.String("maxversion", + "0x"+strconv.FormatInt(int64(VersionTLS10), 16), + "maximum version to negotiate") var testClientAuth = flag.Int("clientauth", 0, "value for tls.Config.ClientAuth") func GetTestConfig() *Config { var config = *testConfig + + minVersion, err := strconv.ParseUint(*testMinVersion, 0, 64) + if err != nil { + panic(err) + } + config.MinVersion = uint16(minVersion) + maxVersion, err := strconv.ParseUint(*testMaxVersion, 0, 64) + if err != nil { + panic(err) + } + config.MaxVersion = uint16(maxVersion) + suites := strings.Split(*testCipherSuites, ",") config.CipherSuites = make([]uint16, len(suites)) for i := range suites { @@ -345,6 +486,25 @@ func GetTestConfig() *Config { config.CipherSuites[i] = uint16(suite) } + ecdsa := false + for _, suite := range config.CipherSuites { + switch suite { + case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: + ecdsa = true + } + } + if ecdsa { + config.Certificates = nil + if !*connect { + config.Certificates = make([]Certificate, 1) + config.Certificates[0].Certificate = [][]byte{testECDSACertificate} + config.Certificates[0].PrivateKey = testECDSAPrivateKey + } + config.BuildNameToCertificate() + } + config.ClientAuth = ClientAuthType(*testClientAuth) return &config } @@ -403,11 +563,13 @@ func fromHex(s string) []byte { return b } -var testCertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9") +var testRSACertificate = fromHex("308202b030820219a00302010202090085b0bba48a7fb8ca300d06092a864886f70d01010505003045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3130303432343039303933385a170d3131303432343039303933385a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819f300d06092a864886f70d010101050003818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a381a73081a4301d0603551d0e04160414b1ade2855acfcb28db69ce2369ded3268e18883930750603551d23046e306c8014b1ade2855acfcb28db69ce2369ded3268e188839a149a4473045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746482090085b0bba48a7fb8ca300c0603551d13040530030101ff300d06092a864886f70d010105050003818100086c4524c76bb159ab0c52ccf2b014d7879d7a6475b55a9566e4c52b8eae12661feb4f38b36e60d392fdf74108b52513b1187a24fb301dbaed98b917ece7d73159db95d31d78ea50565cd5825a2d5a5f33c4b6d8c97590968c0f5298b5cd981f89205ff2a01ca31b9694dda9fd57e970e8266d71999b266e3850296c90a7bdd9") + +var testECDSACertificate = fromHex("3082020030820162020900b8bf2d47a0d2ebf4300906072a8648ce3d04013045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c7464301e170d3132313132323135303633325a170d3232313132303135303633325a3045310b3009060355040613024155311330110603550408130a536f6d652d53746174653121301f060355040a1318496e7465726e6574205769646769747320507479204c746430819b301006072a8648ce3d020106052b81040023038186000400c4a1edbe98f90b4873367ec316561122f23d53c33b4d213dcd6b75e6f6b0dc9adf26c1bcb287f072327cb3642f1c90bcea6823107efee325c0483a69e0286dd33700ef0462dd0da09c706283d881d36431aa9e9731bd96b068c09b23de76643f1a5c7fe9120e5858b65f70dd9bd8ead5d7f5d5ccb9b69f30665b669a20e227e5bffe3b300906072a8648ce3d040103818c0030818802420188a24febe245c5487d1bacf5ed989dae4770c05e1bb62fbdf1b64db76140d311a2ceee0b7e927eff769dc33b7ea53fcefa10e259ec472d7cacda4e970e15a06fd00242014dfcbe67139c2d050ebd3fa38c25c13313830d9406bbd4377af6ec7ac9862eddd711697f857c56defb31782be4c7780daecbbe9e4e3624317b6a0f399512078f2a") var testSNICertificate = fromHex("308201f23082015da003020102020100300b06092a864886f70d01010530283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d301e170d3132303431313137343033355a170d3133303431313137343533355a30283110300e060355040a130741636d6520436f311430120603550403130b736e69746573742e636f6d30819d300b06092a864886f70d01010103818d0030818902818100bb79d6f517b5e5bf4610d0dc69bee62b07435ad0032d8a7a4385b71452e7a5654c2c78b8238cb5b482e5de1f953b7e62a52ca533d6fe125c7a56fcf506bffa587b263fb5cd04d3d0c921964ac7f4549f5abfef427100fe1899077f7e887d7df10439c4a22edb51c97ce3c04c3b326601cfafb11db8719a1ddbdb896baeda2d790203010001a3323030300e0603551d0f0101ff0404030200a0300d0603551d0e0406040401020304300f0603551d2304083006800401020304300b06092a864886f70d0101050381810089c6455f1c1f5ef8eb1ab174ee2439059f5c4259bb1a8d86cdb1d056f56a717da40e95ab90f59e8deaf627c157995094db0802266eb34fc6842dea8a4b68d9c1389103ab84fb9e1f85d9b5d23ff2312c8670fbb540148245a4ebafe264d90c8a4cf4f85b0fac12ac2fc4a3154bad52462868af96c62c6525d652b6e31845bdcc") -var testPrivateKey = &rsa.PrivateKey{ +var testRSAPrivateKey = &rsa.PrivateKey{ PublicKey: rsa.PublicKey{ N: bigFromString("131650079503776001033793877885499001334664249354723305978524647182322416328664556247316495448366990052837680518067798333412266673813370895702118944398081598789828837447552603077848001020611640547221687072142537202428102790818451901395596882588063427854225330436740647715202971973145151161964464812406232198521"), E: 65537, @@ -419,6 +581,22 @@ var testPrivateKey = &rsa.PrivateKey{ }, } +var testECDSAPrivateKey = &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: &elliptic.CurveParams{ + P: bigFromString("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"), + N: bigFromString("6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449"), + B: bigFromString("1093849038073734274511112390766805569936207598951683748994586394495953116150735016013708737573759623248592132296706313309438452531591012912142327488478985984"), + Gx: bigFromString("2661740802050217063228768716723360960729859168756973147706671368418802944996427808491545080627771902352094241225065558662157113545570916814161637315895999846"), + Gy: bigFromString("3757180025770020463545507224491183603594455134769762486694567779615544477440556316691234405012945539562144444537289428522585666729196580810124344277578376784"), + BitSize: 521, + }, + X: bigFromString("2636411247892461147287360222306590634450676461695221912739908880441342231985950069527906976759812296359387337367668045707086543273113073382714101597903639351"), + Y: bigFromString("3204695818431246682253994090650952614555094516658732116404513121125038617915183037601737180082382202488628239201196033284060130040574800684774115478859677243"), + }, + D: bigFromString("5477294338614160138026852784385529180817726002953041720191098180813046231640184669647735805135001309477695746518160084669446643325196003346204701381388769751"), +} + func loadPEMCert(in string) *x509.Certificate { block, _ := pem.Decode([]byte(in)) if block.Type == "CERTIFICATE" && len(block.Headers) == 0 { @@ -435,7 +613,7 @@ func loadPEMCert(in string) *x509.Certificate { // The values for this test are obtained by building and running in server mode: // % go test -test.run "TestRunServer" -serve // The recorded bytes are written to stdout. -var rc4ServerScript = [][]byte{ +var rsaRC4ServerScript = [][]byte{ { 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00, 0x50, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xbd, 0x32, @@ -592,7 +770,7 @@ var rc4ServerScript = [][]byte{ }, } -var des3ServerScript = [][]byte{ +var rsaDES3ServerScript = [][]byte{ { 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5d, 0x38, 0xec, @@ -801,7 +979,7 @@ var des3ServerScript = [][]byte{ }, } -var aesServerScript = [][]byte{ +var rsaAESServerScript = [][]byte{ { 0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00, 0xc1, 0x03, 0x03, 0x50, 0xae, 0x5c, 0xe9, 0x5e, @@ -1027,6 +1205,216 @@ var aesServerScript = [][]byte{ }, } +// Generated using: +// $ go test -test.run TestRunServer -serve -ciphersuites=0xc00a +// $ openssl s_client -host 127.0.0.1 -port 10443 -cipher ECDHE-ECDSA-AES256-SHA +var ecdheECDSAAESServerScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0xa0, 0x01, 0x00, 0x00, + 0x9c, 0x03, 0x03, 0x50, 0xd7, 0x18, 0x31, 0x49, + 0xde, 0x19, 0x8d, 0x08, 0x5c, 0x4b, 0x60, 0x67, + 0x0f, 0xfe, 0xd0, 0x62, 0xf9, 0x31, 0x48, 0x17, + 0x9e, 0x50, 0xc1, 0xd8, 0x35, 0x24, 0x0e, 0xa6, + 0x09, 0x06, 0x51, 0x00, 0x00, 0x04, 0xc0, 0x0a, + 0x00, 0xff, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, + 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, + 0x00, 0x34, 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, + 0x00, 0x09, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, + 0x00, 0x15, 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, + 0x00, 0x13, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, + 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, + 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, + 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, + 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, + 0x00, 0x0f, 0x00, 0x01, 0x01, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0a, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0x0e, 0x0b, 0x00, 0x02, 0x0a, 0x00, 0x02, + 0x07, 0x00, 0x02, 0x04, 0x30, 0x82, 0x02, 0x00, + 0x30, 0x82, 0x01, 0x62, 0x02, 0x09, 0x00, 0xb8, + 0xbf, 0x2d, 0x47, 0xa0, 0xd2, 0xeb, 0xf4, 0x30, + 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x04, 0x01, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x31, + 0x31, 0x32, 0x32, 0x31, 0x35, 0x30, 0x36, 0x33, + 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x31, 0x31, + 0x32, 0x30, 0x31, 0x35, 0x30, 0x36, 0x33, 0x32, + 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, + 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, + 0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, + 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86, + 0x00, 0x04, 0x00, 0xc4, 0xa1, 0xed, 0xbe, 0x98, + 0xf9, 0x0b, 0x48, 0x73, 0x36, 0x7e, 0xc3, 0x16, + 0x56, 0x11, 0x22, 0xf2, 0x3d, 0x53, 0xc3, 0x3b, + 0x4d, 0x21, 0x3d, 0xcd, 0x6b, 0x75, 0xe6, 0xf6, + 0xb0, 0xdc, 0x9a, 0xdf, 0x26, 0xc1, 0xbc, 0xb2, + 0x87, 0xf0, 0x72, 0x32, 0x7c, 0xb3, 0x64, 0x2f, + 0x1c, 0x90, 0xbc, 0xea, 0x68, 0x23, 0x10, 0x7e, + 0xfe, 0xe3, 0x25, 0xc0, 0x48, 0x3a, 0x69, 0xe0, + 0x28, 0x6d, 0xd3, 0x37, 0x00, 0xef, 0x04, 0x62, + 0xdd, 0x0d, 0xa0, 0x9c, 0x70, 0x62, 0x83, 0xd8, + 0x81, 0xd3, 0x64, 0x31, 0xaa, 0x9e, 0x97, 0x31, + 0xbd, 0x96, 0xb0, 0x68, 0xc0, 0x9b, 0x23, 0xde, + 0x76, 0x64, 0x3f, 0x1a, 0x5c, 0x7f, 0xe9, 0x12, + 0x0e, 0x58, 0x58, 0xb6, 0x5f, 0x70, 0xdd, 0x9b, + 0xd8, 0xea, 0xd5, 0xd7, 0xf5, 0xd5, 0xcc, 0xb9, + 0xb6, 0x9f, 0x30, 0x66, 0x5b, 0x66, 0x9a, 0x20, + 0xe2, 0x27, 0xe5, 0xbf, 0xfe, 0x3b, 0x30, 0x09, + 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, + 0x01, 0x03, 0x81, 0x8c, 0x00, 0x30, 0x81, 0x88, + 0x02, 0x42, 0x01, 0x88, 0xa2, 0x4f, 0xeb, 0xe2, + 0x45, 0xc5, 0x48, 0x7d, 0x1b, 0xac, 0xf5, 0xed, + 0x98, 0x9d, 0xae, 0x47, 0x70, 0xc0, 0x5e, 0x1b, + 0xb6, 0x2f, 0xbd, 0xf1, 0xb6, 0x4d, 0xb7, 0x61, + 0x40, 0xd3, 0x11, 0xa2, 0xce, 0xee, 0x0b, 0x7e, + 0x92, 0x7e, 0xff, 0x76, 0x9d, 0xc3, 0x3b, 0x7e, + 0xa5, 0x3f, 0xce, 0xfa, 0x10, 0xe2, 0x59, 0xec, + 0x47, 0x2d, 0x7c, 0xac, 0xda, 0x4e, 0x97, 0x0e, + 0x15, 0xa0, 0x6f, 0xd0, 0x02, 0x42, 0x01, 0x4d, + 0xfc, 0xbe, 0x67, 0x13, 0x9c, 0x2d, 0x05, 0x0e, + 0xbd, 0x3f, 0xa3, 0x8c, 0x25, 0xc1, 0x33, 0x13, + 0x83, 0x0d, 0x94, 0x06, 0xbb, 0xd4, 0x37, 0x7a, + 0xf6, 0xec, 0x7a, 0xc9, 0x86, 0x2e, 0xdd, 0xd7, + 0x11, 0x69, 0x7f, 0x85, 0x7c, 0x56, 0xde, 0xfb, + 0x31, 0x78, 0x2b, 0xe4, 0xc7, 0x78, 0x0d, 0xae, + 0xcb, 0xbe, 0x9e, 0x4e, 0x36, 0x24, 0x31, 0x7b, + 0x6a, 0x0f, 0x39, 0x95, 0x12, 0x07, 0x8f, 0x2a, + 0x16, 0x03, 0x01, 0x01, 0x1a, 0x0c, 0x00, 0x01, + 0x16, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39, + 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27, + 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99, + 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0, + 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46, + 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc, + 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b, + 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c, + 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6, + 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d, + 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28, + 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a, + 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07, + 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0, + 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea, + 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f, + 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79, + 0x90, 0x33, 0x00, 0x8b, 0x30, 0x81, 0x88, 0x02, + 0x42, 0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, + 0x04, 0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, + 0x95, 0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, + 0x3f, 0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, + 0x4d, 0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, + 0xe7, 0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, + 0xff, 0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, + 0x6a, 0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, + 0xe5, 0xbd, 0x66, 0x02, 0x42, 0x00, 0xad, 0x7d, + 0x06, 0x35, 0xab, 0xec, 0x8d, 0xac, 0xd4, 0xba, + 0x1b, 0x49, 0x5e, 0x05, 0x5f, 0xf0, 0x97, 0x93, + 0x82, 0xb8, 0x2b, 0x8d, 0x91, 0x98, 0x63, 0x8e, + 0xb4, 0x14, 0x62, 0xdb, 0x1e, 0xc9, 0x2b, 0x30, + 0xf8, 0x41, 0x9b, 0xa6, 0xe6, 0xbc, 0xde, 0x0e, + 0x68, 0x30, 0x22, 0x50, 0xe6, 0x98, 0x97, 0x7b, + 0x69, 0xf7, 0x93, 0xed, 0xcd, 0x19, 0x2f, 0x44, + 0x6c, 0x2e, 0xdf, 0x25, 0xee, 0xcc, 0x46, 0x16, + 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x8a, 0x10, 0x00, 0x00, + 0x86, 0x85, 0x04, 0x00, 0x1c, 0xc5, 0xe8, 0xb3, + 0x42, 0xb4, 0xad, 0xca, 0x45, 0xcd, 0x42, 0x7b, + 0xfb, 0x0c, 0xea, 0x32, 0x26, 0xd4, 0x8a, 0xef, + 0xdf, 0xc9, 0xff, 0xd2, 0xe0, 0x36, 0xea, 0x4e, + 0xbb, 0x3e, 0xf4, 0x9c, 0x76, 0x4f, 0x44, 0xbd, + 0x84, 0x72, 0xdd, 0xcb, 0xe5, 0x28, 0x8d, 0x31, + 0x72, 0x3b, 0xd3, 0xf2, 0x9a, 0x13, 0xfb, 0x8a, + 0xa7, 0x72, 0xca, 0x21, 0x6c, 0xea, 0xbf, 0xe9, + 0x8c, 0x0a, 0xcc, 0x8f, 0xd6, 0x00, 0x20, 0x87, + 0xf3, 0x7d, 0x18, 0xc5, 0xfd, 0x9e, 0xdd, 0x6b, + 0x06, 0xdc, 0x52, 0xeb, 0x14, 0xc0, 0x67, 0x5a, + 0x06, 0xd8, 0x98, 0x19, 0x14, 0xe7, 0xd4, 0x36, + 0x32, 0xee, 0xb7, 0xfa, 0xe2, 0x85, 0x4a, 0x16, + 0x42, 0x0c, 0xa6, 0x21, 0xcf, 0x1f, 0xae, 0x10, + 0x8b, 0x28, 0x32, 0x19, 0xa4, 0x0a, 0xd7, 0xce, + 0xe6, 0xe1, 0x93, 0xfb, 0x5f, 0x08, 0x8b, 0x42, + 0xa2, 0x20, 0xed, 0x0d, 0x62, 0xca, 0xed, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x30, 0x2e, 0x33, 0xc0, 0x57, 0x6c, 0xb4, + 0x1b, 0xd2, 0x63, 0xe8, 0x67, 0x10, 0x2d, 0x87, + 0x71, 0x6e, 0x19, 0x60, 0xf4, 0xa4, 0x10, 0x52, + 0x73, 0x2d, 0x09, 0x5e, 0xdb, 0x6c, 0xdc, 0xcf, + 0x2d, 0xff, 0x03, 0x11, 0x95, 0x76, 0x90, 0xd7, + 0x87, 0x54, 0x43, 0xed, 0xc2, 0x36, 0x69, 0x14, + 0x72, 0x4a, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x8b, 0xde, 0xef, 0xba, 0xc5, 0x7e, 0x04, + 0xab, 0xfd, 0x79, 0x56, 0xf3, 0xe1, 0xa5, 0x3e, + 0x02, 0xdf, 0x69, 0x6d, 0x1f, 0x41, 0x9f, 0xbc, + 0x93, 0xe2, 0x6c, 0xf1, 0xb1, 0x38, 0xf5, 0x2b, + 0x8c, 0x4c, 0xf4, 0x74, 0xe1, 0x79, 0x35, 0x34, + 0x97, 0x9b, 0xd5, 0xba, 0xfd, 0xf7, 0x2f, 0x2d, + 0x9e, 0x84, 0x54, 0xee, 0x77, 0x59, 0x23, 0x8f, + 0xc8, 0x84, 0xb4, 0xd6, 0xea, 0x4c, 0x44, 0x8a, + 0xc6, 0x9c, 0xf9, 0x9b, 0x27, 0xea, 0x4f, 0x28, + 0x72, 0x33, 0x12, 0x20, 0x7c, 0xd7, 0x3f, 0x56, + 0xa6, 0x76, 0xc7, 0x48, 0xe4, 0x2d, 0x6f, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x30, 0x36, 0xe3, 0xd4, 0xf7, 0xb1, 0x69, + 0x18, 0x8d, 0x09, 0xba, 0x52, 0x1e, 0xd5, 0x7d, + 0x2c, 0x15, 0x3a, 0xd6, 0xe3, 0x99, 0x30, 0x2c, + 0x99, 0x97, 0xbc, 0x19, 0x3c, 0x63, 0xa1, 0x25, + 0x68, 0xbc, 0x8a, 0x16, 0x47, 0xec, 0xae, 0x13, + 0xa4, 0x03, 0x96, 0x29, 0x11, 0x92, 0x90, 0x1a, + 0xc8, 0xa4, 0x17, 0x03, 0x01, 0x00, 0x20, 0xc1, + 0x10, 0x1d, 0xa6, 0xf1, 0xe2, 0x8a, 0xcc, 0x37, + 0x7d, 0x8e, 0x05, 0x00, 0xfb, 0xd1, 0x9f, 0xc7, + 0x11, 0xd2, 0x00, 0xb4, 0x27, 0x0a, 0x25, 0x14, + 0xd9, 0x79, 0x1b, 0xcb, 0x4d, 0x81, 0x61, 0x17, + 0x03, 0x01, 0x00, 0x30, 0x5c, 0x7c, 0x2d, 0xc0, + 0x9e, 0xa6, 0xc4, 0x8e, 0xfd, 0xf4, 0xe2, 0xe5, + 0xe4, 0xe6, 0x56, 0x9f, 0x7d, 0x4c, 0x4c, 0x2d, + 0xb7, 0xa9, 0xac, 0xfa, 0x9f, 0x12, 0x7f, 0x2d, + 0x30, 0x57, 0xe4, 0x8e, 0x30, 0x86, 0x65, 0x59, + 0xcd, 0x24, 0xda, 0xe2, 0x8a, 0x7b, 0x0c, 0x5e, + 0x86, 0x05, 0x06, 0x2a, 0x15, 0x03, 0x01, 0x00, + 0x20, 0xd6, 0xb7, 0x70, 0xf8, 0x47, 0xbc, 0x0f, + 0xf4, 0x66, 0x98, 0x1b, 0x1e, 0x8a, 0x8c, 0x0b, + 0xa1, 0x4a, 0x04, 0x29, 0x60, 0x72, 0x8b, 0xc4, + 0x73, 0xc1, 0xd6, 0x41, 0x72, 0xb7, 0x17, 0x39, + 0xda, + }, +} + var sslv3ServerScript = [][]byte{ { 0x16, 0x03, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, @@ -1559,38 +1947,115 @@ var serverResumeTest = [][]byte{ }, } -var clientauthTests = []clientauthTest{ +var clientauthRSATests = []clientauthTest{ // Server asks for cert with empty CA list, client doesn't give it. // go test -run "TestRunServer" -serve -clientauth 1 {"RequestClientCert, none given", RequestClientCert, nil, [][]byte{ { - 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00, - 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x9e, 0x31, - 0xe6, 0x36, 0x5e, 0x5e, 0x24, 0xe4, 0x0d, 0x26, - 0x34, 0xa7, 0x1c, 0x2e, 0x59, 0x6d, 0xa5, 0x3e, - 0x72, 0xf3, 0xa3, 0x1c, 0xbc, 0xb3, 0x27, 0xaf, - 0x92, 0x5b, 0x7d, 0x00, 0x00, 0x28, 0x00, 0x39, - 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, - 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, - 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, - 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, - 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, - 0x00, + 0x16, 0x03, 0x01, 0x01, 0x1e, 0x01, 0x00, 0x01, + 0x1a, 0x03, 0x03, 0x51, 0xe5, 0x6c, 0xb5, 0x5a, + 0xc2, 0xf5, 0xf0, 0x92, 0x94, 0x8a, 0x64, 0x18, + 0xa4, 0x2b, 0x82, 0x07, 0xbc, 0xd9, 0xd9, 0xf9, + 0x7b, 0xd2, 0xd0, 0xee, 0xa2, 0x70, 0x4e, 0x23, + 0x88, 0x7c, 0x95, 0x00, 0x00, 0x82, 0xc0, 0x30, + 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14, + 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b, + 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32, + 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f, + 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35, + 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13, + 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f, + 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13, + 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67, + 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31, + 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e, + 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f, + 0x00, 0x07, 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, + 0xc0, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, + 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, + 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04, + 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34, + 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, + 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, + 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, + 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01, + 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, + 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, + 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f, + 0x00, 0x01, 0x01, }, { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, @@ -1599,158 +2064,200 @@ var clientauthTests = []clientauthTest{ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, - 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, - 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x01, 0x00, 0x09, 0x0d, 0x00, 0x00, + 0x05, 0x02, 0x01, 0x40, 0x00, 0x00, 0x16, 0x03, + 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, }, { 0x16, 0x03, 0x01, 0x00, 0x07, 0x0b, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, - 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x04, - 0x58, 0x63, 0x26, 0x32, 0x1b, 0x34, 0xbe, 0x10, - 0xe4, 0xe4, 0x3e, 0xcd, 0x36, 0x7f, 0xa8, 0xa8, - 0xe0, 0x19, 0xe8, 0x94, 0x13, 0xd9, 0x35, 0xc4, - 0x71, 0xb4, 0x91, 0xd4, 0xbc, 0x74, 0x57, 0x9f, - 0x93, 0xb7, 0x5d, 0x3b, 0x9c, 0xff, 0x5d, 0x79, - 0xdb, 0x86, 0xfc, 0xdc, 0x74, 0x1e, 0x0c, 0xc6, - 0xe8, 0x93, 0xcf, 0xaf, 0xba, 0x1d, 0xfd, 0x8a, - 0xeb, 0xef, 0xbf, 0xfa, 0xa6, 0xe7, 0x53, 0x98, - 0x60, 0x4e, 0x0e, 0x60, 0x7d, 0xea, 0x40, 0x8d, - 0x1d, 0x8f, 0xa3, 0xc6, 0x83, 0xbc, 0xef, 0xb7, - 0x9a, 0x4a, 0xe7, 0x99, 0xee, 0x0b, 0xc7, 0x46, - 0x75, 0x45, 0x66, 0xe8, 0x5f, 0x4b, 0x08, 0xa4, - 0xc1, 0x36, 0xd0, 0x36, 0x2c, 0xf2, 0x9a, 0x44, - 0x1e, 0x5f, 0x22, 0xf4, 0xbe, 0x66, 0x66, 0x17, - 0xd8, 0xb6, 0x0a, 0x89, 0xed, 0x22, 0x80, 0xdb, - 0xad, 0x05, 0xd1, 0xb5, 0x93, 0xa1, 0x1c, 0x14, + 0x86, 0x10, 0x00, 0x00, 0x82, 0x00, 0x80, 0x36, + 0xfc, 0xd8, 0xc8, 0xa2, 0x67, 0xc8, 0xc6, 0xf4, + 0x28, 0x70, 0xe1, 0x5a, 0x02, 0x8f, 0xef, 0x42, + 0xe0, 0xd3, 0xb8, 0xd6, 0x6b, 0xe4, 0xee, 0x5c, + 0xcf, 0x42, 0xc4, 0xfa, 0xcd, 0x0f, 0xfe, 0xf4, + 0x76, 0x76, 0x47, 0x73, 0xa8, 0x72, 0x8f, 0xa2, + 0x56, 0x81, 0x83, 0xb8, 0x84, 0x72, 0x67, 0xdd, + 0xbe, 0x05, 0x4b, 0x84, 0xd9, 0xd2, 0xb6, 0xc2, + 0xe7, 0x20, 0xac, 0x1f, 0x46, 0x9d, 0x05, 0x47, + 0x8e, 0x89, 0xc0, 0x42, 0x57, 0x4a, 0xa2, 0x98, + 0xe5, 0x39, 0x4f, 0xc4, 0x27, 0x6d, 0x43, 0xa8, + 0x83, 0x76, 0xe6, 0xad, 0xe3, 0x17, 0x68, 0x31, + 0xcb, 0x7e, 0xfc, 0xe7, 0x4b, 0x76, 0x3d, 0x3c, + 0xfa, 0x77, 0x65, 0xc9, 0x4c, 0x5b, 0xce, 0x5e, + 0xf7, 0x8b, 0xa8, 0xa6, 0xdd, 0xb2, 0xef, 0x0b, + 0x46, 0x83, 0xdf, 0x0a, 0x8c, 0x22, 0x12, 0x6e, + 0xe1, 0x45, 0x54, 0x88, 0xd1, 0xe8, 0xd2, 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, - 0x00, 0x24, 0x62, 0x6f, 0x3d, 0x30, 0x56, 0x97, - 0xde, 0x03, 0x67, 0xa9, 0x63, 0x21, 0xb6, 0xe6, - 0x05, 0x69, 0x94, 0xfb, 0x50, 0xc1, 0x99, 0xdd, - 0xf6, 0xe8, 0x60, 0xbd, 0xe6, 0xba, 0xe3, 0x50, - 0x0a, 0xcd, 0xde, 0x14, 0x16, 0xc4, + 0x00, 0x24, 0x30, 0x8c, 0x7d, 0x40, 0xfc, 0x5e, + 0x80, 0x9c, 0xc4, 0x7c, 0x62, 0x01, 0xa1, 0x37, + 0xcf, 0x1a, 0x75, 0x28, 0x8d, 0xeb, 0x63, 0xcc, + 0x02, 0xa6, 0x66, 0xdf, 0x36, 0x01, 0xb3, 0x9d, + 0x38, 0x42, 0x16, 0x91, 0xf0, 0x02, }, { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0xf0, 0x21, 0xf6, 0x84, 0x6a, - 0xe3, 0x6b, 0x8a, 0xc5, 0x46, 0x50, 0xca, 0x40, - 0xea, 0x4e, 0x82, 0xc1, 0x70, 0x25, 0xd8, 0x7d, - 0x60, 0xf5, 0x51, 0x7f, 0x64, 0x03, 0x9f, 0x53, - 0xec, 0xfb, 0x57, 0xa9, 0xfc, 0x26, 0x15, 0x17, - 0x03, 0x01, 0x00, 0x21, 0xa6, 0xc6, 0x94, 0x2b, - 0xa9, 0xcb, 0x93, 0xff, 0xb6, 0xa6, 0xe7, 0xc5, - 0x37, 0x86, 0x15, 0x37, 0x57, 0xce, 0xef, 0x54, - 0x96, 0x5d, 0x50, 0xa0, 0x50, 0x69, 0x5e, 0x82, - 0x61, 0x8d, 0x42, 0xfb, 0x78, 0x15, 0x03, 0x01, - 0x00, 0x16, 0x45, 0xd1, 0x86, 0x68, 0x59, 0xc1, - 0xaf, 0xac, 0x5c, 0x46, 0x8a, 0x68, 0x69, 0x0c, - 0xd7, 0x67, 0xbf, 0xf0, 0x3e, 0xee, 0x45, 0x55, + 0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0x96, 0x9a, 0x2a, + 0x6c, 0x8c, 0x7e, 0x38, 0x10, 0x46, 0x86, 0x1d, + 0x19, 0x1d, 0x62, 0x29, 0x3f, 0x58, 0xfb, 0x6d, + 0x89, 0xd2, 0x81, 0x9a, 0x1c, 0xb3, 0x58, 0xb3, + 0x19, 0x39, 0x17, 0x47, 0x49, 0xc9, 0xfe, 0x4a, + 0x7a, 0x32, 0xac, 0x2c, 0x43, 0xf9, 0xa9, 0xea, + 0xec, 0x51, 0x46, 0xf1, 0xb8, 0x59, 0x23, 0x70, + 0xce, 0x7c, 0xb9, 0x47, 0x70, 0xa3, 0xc9, 0xae, + 0x47, 0x7b, 0x7e, 0xc7, 0xcf, 0x76, 0x12, 0x76, + 0x18, 0x90, 0x12, 0xcd, 0xf3, 0xd4, 0x27, 0x81, + 0xfc, 0x46, 0x03, 0x3e, 0x05, 0x87, 0x6f, 0x14, + 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, + 0x00, 0x24, 0xc3, 0xa0, 0x29, 0xb1, 0x52, 0x82, + 0xef, 0x85, 0xa1, 0x64, 0x0f, 0xe4, 0xa3, 0xfb, + 0xa7, 0x1d, 0x22, 0x4c, 0xcb, 0xd6, 0x5b, 0x18, + 0x61, 0xc7, 0x7c, 0xf2, 0x67, 0x4a, 0xc7, 0x11, + 0x9d, 0x8e, 0x0e, 0x15, 0x22, 0xcf, 0x17, 0x03, + 0x01, 0x00, 0x21, 0xfd, 0xbb, 0xf1, 0xa9, 0x7c, + 0xbf, 0x92, 0xb3, 0xfa, 0x2c, 0x08, 0x6f, 0x22, + 0x78, 0x80, 0xf2, 0x2e, 0x86, 0x26, 0x21, 0x36, + 0x3f, 0x32, 0xdf, 0xb6, 0x47, 0xa5, 0xf8, 0x27, + 0xc1, 0xe9, 0x53, 0x90, 0x15, 0x03, 0x01, 0x00, + 0x16, 0xfe, 0xef, 0x2e, 0xa0, 0x5d, 0xe0, 0xce, + 0x94, 0x20, 0x56, 0x61, 0x6e, 0xe5, 0x62, 0xce, + 0x27, 0x57, 0x3e, 0x30, 0x32, 0x77, 0x53, }, }}, + // Server asks for cert with empty CA list, client gives one // go test -run "TestRunServer" -serve -clientauth 1 {"RequestClientCert, client gives it", RequestClientCert, []*x509.Certificate{clientCertificate}, [][]byte{ { - 0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00, - 0x50, 0x03, 0x01, 0x50, 0x77, 0x43, 0x47, 0xfd, - 0x1d, 0xb0, 0x60, 0x4c, 0x25, 0x86, 0x45, 0x4a, - 0xe5, 0x3f, 0x80, 0x56, 0x18, 0x91, 0x5c, 0xe2, - 0x62, 0xc5, 0x77, 0xc2, 0x92, 0xdd, 0xdc, 0x39, - 0x23, 0x1d, 0xc5, 0x00, 0x00, 0x28, 0x00, 0x39, - 0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13, - 0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f, - 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, - 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, - 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01, - 0x00, + 0x16, 0x03, 0x01, 0x01, 0x1e, 0x01, 0x00, 0x01, + 0x1a, 0x03, 0x03, 0x51, 0xe5, 0x74, 0x0e, 0x95, + 0x6f, 0x4f, 0x4a, 0xbf, 0xb7, 0xc0, 0x6c, 0xac, + 0xd9, 0xfe, 0x7d, 0xd0, 0x51, 0x19, 0x62, 0x62, + 0x1c, 0x6e, 0x57, 0x77, 0xd2, 0x31, 0xaf, 0x88, + 0xb9, 0xc0, 0x1d, 0x00, 0x00, 0x82, 0xc0, 0x30, + 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14, + 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b, + 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32, + 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f, + 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35, + 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13, + 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f, + 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13, + 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67, + 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31, + 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e, + 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f, + 0x00, 0x07, 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, + 0xc0, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, + 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, + 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04, + 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34, + 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, + 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, + 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, + 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01, + 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, + 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, + 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f, + 0x00, 0x01, 0x01, }, { - 0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00, - 0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16, - 0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, - 0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, - 0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, - 0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, - 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, @@ -1759,82 +2266,31 @@ var clientauthTests = []clientauthTest{ 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, - 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, - 0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, - 0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, - 0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, - 0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, - 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, - 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, - 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, - 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, - 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, - 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, - 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, - 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, - 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, - 0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, - 0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, - 0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, - 0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, - 0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, - 0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, - 0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, - 0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, - 0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, - 0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, - 0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, - 0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, - 0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, - 0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, - 0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, - 0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, - 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, - 0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, - 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, - 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, - 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, - 0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, - 0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, - 0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, - 0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, - 0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, - 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, - 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, - 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, - 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, - 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, - 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, - 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, - 0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, - 0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, - 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, - 0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, - 0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, - 0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, - 0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, - 0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, - 0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, - 0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, - 0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, - 0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, - 0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, - 0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, - 0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, - 0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, - 0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, - 0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, - 0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, - 0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x08, 0x0d, - 0x00, 0x00, 0x04, 0x01, 0x01, 0x00, 0x00, 0x16, - 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x01, 0x00, 0x09, 0x0d, 0x00, 0x00, + 0x05, 0x02, 0x01, 0x40, 0x00, 0x00, 0x16, 0x03, + 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, }, { 0x16, 0x03, 0x01, 0x01, 0xfb, 0x0b, 0x00, 0x01, @@ -1902,66 +2358,778 @@ var clientauthTests = []clientauthTest{ 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf, 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85, 0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00, - 0x82, 0x00, 0x80, 0x81, 0x46, 0x43, 0xf9, 0xe7, - 0xda, 0x8c, 0x92, 0x3a, 0x78, 0x1a, 0x86, 0xb3, - 0xbe, 0x83, 0x22, 0xb6, 0xaa, 0x57, 0x37, 0x68, - 0x9e, 0x54, 0x3f, 0xd3, 0xce, 0x4d, 0x5e, 0x2a, - 0xdc, 0xb0, 0x49, 0x02, 0xbb, 0xc0, 0x45, 0x58, - 0x79, 0x10, 0xc7, 0x94, 0x60, 0x9f, 0x1b, 0x5f, - 0x18, 0x31, 0x37, 0x9c, 0xe0, 0xe6, 0xdf, 0x5e, - 0x70, 0x44, 0xf6, 0x8b, 0xdf, 0xf1, 0xf6, 0x43, - 0xc8, 0x2f, 0xd1, 0xce, 0xd0, 0xd6, 0x64, 0x4f, - 0xe8, 0x2b, 0xfa, 0xd3, 0xd0, 0xd1, 0x2e, 0xaa, - 0x9b, 0x1d, 0x13, 0x5c, 0xbe, 0x57, 0x41, 0x6c, - 0x5e, 0x8d, 0xea, 0xa9, 0x3c, 0x58, 0xa0, 0x30, - 0x92, 0x77, 0x7a, 0xed, 0x64, 0x58, 0xe5, 0x7f, - 0x6a, 0x93, 0x89, 0x66, 0x3d, 0x13, 0x16, 0x56, - 0xa0, 0xad, 0xdc, 0x68, 0x95, 0x87, 0x81, 0xd0, - 0x90, 0x4d, 0x5f, 0xfe, 0x3e, 0x83, 0x15, 0x2e, - 0x50, 0x3c, 0xdd, 0x16, 0x03, 0x01, 0x00, 0x86, - 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x2b, 0xf8, - 0x56, 0x48, 0xbb, 0x02, 0x37, 0x15, 0x02, 0x74, - 0x33, 0x53, 0x65, 0xa7, 0x7c, 0x2f, 0xc6, 0x5d, - 0x80, 0x59, 0xc1, 0xc2, 0x3b, 0xa9, 0xde, 0x4e, - 0x70, 0x51, 0xd2, 0xde, 0x58, 0x7f, 0xd8, 0xb9, - 0xb6, 0x3b, 0xc8, 0xaa, 0xfc, 0x3d, 0x53, 0x2d, - 0x61, 0x4d, 0xf5, 0x60, 0x12, 0xc2, 0xa5, 0x39, - 0x0c, 0xa7, 0xc6, 0xac, 0x26, 0x4b, 0xf4, 0x5f, - 0xe9, 0xf4, 0xf2, 0x73, 0x48, 0xe4, 0x3b, 0xee, - 0xf2, 0xee, 0xc0, 0xee, 0xfb, 0x5b, 0x60, 0xc2, - 0x74, 0xe6, 0xf6, 0x43, 0x3e, 0xa4, 0xf7, 0x97, - 0x3d, 0xfc, 0xe9, 0x44, 0x21, 0x18, 0x46, 0x05, - 0x33, 0xf8, 0xfe, 0x35, 0x5b, 0xe6, 0x8f, 0xef, - 0x4d, 0x4c, 0x87, 0xf6, 0xb4, 0x6e, 0x6b, 0x39, - 0xd8, 0xaa, 0x1b, 0x33, 0xc9, 0x1c, 0x66, 0x48, - 0xbe, 0xfa, 0xb5, 0x92, 0x09, 0xfd, 0xb9, 0xb9, - 0xca, 0xe6, 0x6d, 0x71, 0xc6, 0x89, 0x14, 0x03, + 0x82, 0x00, 0x80, 0x0a, 0x4e, 0x89, 0xdf, 0x3a, + 0x3f, 0xf0, 0x4f, 0xef, 0x1a, 0x90, 0xd4, 0x3c, + 0xaf, 0x10, 0x57, 0xb0, 0xa1, 0x5f, 0xcd, 0x62, + 0x01, 0xe9, 0x0c, 0x36, 0x42, 0xfd, 0xaf, 0x23, + 0xf9, 0x14, 0xa6, 0x72, 0x26, 0x4e, 0x01, 0xdb, + 0xac, 0xb7, 0x4c, 0xe6, 0xa9, 0x52, 0xe2, 0xec, + 0x26, 0x8c, 0x7a, 0x64, 0xf8, 0x0b, 0x4c, 0x2f, + 0xa9, 0xcb, 0x75, 0xaf, 0x60, 0xd4, 0xb4, 0xe6, + 0xe8, 0xdb, 0x78, 0x78, 0x85, 0xf6, 0x0c, 0x95, + 0xcc, 0xb6, 0x55, 0xb9, 0xba, 0x9e, 0x91, 0xbc, + 0x66, 0xdb, 0x1e, 0x28, 0xab, 0x73, 0xce, 0x8b, + 0xd0, 0xd3, 0xe8, 0xbc, 0xd0, 0x21, 0x28, 0xbd, + 0xfb, 0x74, 0x64, 0xde, 0x3b, 0x3b, 0xd3, 0x4c, + 0x32, 0x40, 0x82, 0xba, 0x91, 0x1e, 0xe8, 0x47, + 0xc2, 0x09, 0xb7, 0x16, 0xaa, 0x25, 0xa9, 0x3c, + 0x6c, 0xa7, 0xf8, 0xc9, 0x54, 0x84, 0xc6, 0xf7, + 0x56, 0x05, 0xa4, 0x16, 0x03, 0x01, 0x00, 0x86, + 0x0f, 0x00, 0x00, 0x82, 0x00, 0x80, 0x4b, 0xab, + 0xda, 0xac, 0x2a, 0xb3, 0xe6, 0x34, 0x55, 0xcd, + 0xf2, 0x4b, 0x67, 0xe3, 0xd3, 0xff, 0xa3, 0xf4, + 0x79, 0x82, 0x01, 0x47, 0x8a, 0xe3, 0x9f, 0x89, + 0x70, 0xbe, 0x24, 0x24, 0xb7, 0x69, 0x60, 0xed, + 0x55, 0xa0, 0xca, 0x72, 0xb6, 0x4a, 0xbc, 0x1d, + 0xe2, 0x3f, 0xb5, 0x31, 0xda, 0x02, 0xf6, 0x37, + 0x51, 0xf8, 0x4c, 0x88, 0x2e, 0xb3, 0x8a, 0xe8, + 0x7b, 0x4a, 0x90, 0x36, 0xe4, 0xa6, 0x31, 0x95, + 0x8b, 0xa0, 0xc6, 0x91, 0x12, 0xb9, 0x35, 0x4e, + 0x72, 0xeb, 0x5c, 0xa2, 0xe8, 0x4c, 0x68, 0xf9, + 0x69, 0xfa, 0x70, 0x60, 0x6c, 0x7f, 0x32, 0x99, + 0xf1, 0xc3, 0x2d, 0xb4, 0x59, 0x58, 0x87, 0xaf, + 0x67, 0x62, 0x90, 0xe7, 0x8d, 0xd0, 0xa3, 0x77, + 0x33, 0xc2, 0x9b, 0xd5, 0x9c, 0xc7, 0xea, 0x25, + 0x98, 0x76, 0x9c, 0xe0, 0x6a, 0x03, 0x3a, 0x10, + 0xfd, 0x10, 0x3d, 0x55, 0x53, 0xa0, 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, - 0x24, 0xe3, 0x2b, 0xef, 0x17, 0xd5, 0xa6, 0x4c, - 0x2e, 0x10, 0xac, 0x9c, 0xfe, 0x0f, 0x18, 0x43, - 0x95, 0x00, 0x81, 0xf7, 0x7c, 0x00, 0x5b, 0x89, - 0x52, 0x41, 0xe4, 0x8a, 0x8a, 0x34, 0x31, 0x09, - 0x48, 0x7c, 0xc5, 0xc3, 0x83, + 0x24, 0xd5, 0x12, 0xfc, 0xb9, 0x5a, 0xe3, 0x27, + 0x01, 0xbe, 0xc3, 0x77, 0x17, 0x1a, 0xbb, 0x4f, + 0xae, 0xd5, 0xa7, 0xee, 0x56, 0x61, 0x0d, 0x40, + 0xf4, 0xa4, 0xb5, 0xcc, 0x76, 0xfd, 0xbd, 0x13, + 0x04, 0xe1, 0xb8, 0xc7, 0x36, }, { - 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, - 0x01, 0x00, 0x24, 0x24, 0xaa, 0xaa, 0x56, 0x8b, - 0x41, 0x87, 0x01, 0xbe, 0x80, 0x05, 0x51, 0x36, - 0x08, 0xfc, 0xaf, 0xff, 0x7f, 0xf4, 0x74, 0x84, - 0x88, 0xdc, 0xb8, 0x8e, 0x70, 0x6c, 0x22, 0x04, - 0xee, 0x45, 0x8d, 0xda, 0xed, 0xc6, 0x05, 0x17, - 0x03, 0x01, 0x00, 0x21, 0x91, 0x49, 0x4b, 0xed, - 0xa3, 0x41, 0xe9, 0x88, 0x3b, 0xa3, 0x01, 0xee, - 0x77, 0x4e, 0x12, 0xb4, 0xcd, 0x5e, 0xcc, 0x45, - 0x02, 0x5a, 0x20, 0xd6, 0xe8, 0xac, 0xcb, 0x60, - 0xcb, 0x1b, 0xef, 0xf9, 0xc2, 0x15, 0x03, 0x01, - 0x00, 0x16, 0xd4, 0xcd, 0x92, 0x3c, 0x10, 0x93, - 0x68, 0xc3, 0xdd, 0xaf, 0xe9, 0xcb, 0x5d, 0x94, - 0x1a, 0x06, 0x81, 0xa7, 0x78, 0x0f, 0xc3, 0x03, + 0x16, 0x03, 0x01, 0x02, 0x67, 0x04, 0x00, 0x02, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x4b, 0xd1, 0xef, 0xba, 0x1f, 0xe2, 0x69, + 0x07, 0x7f, 0x85, 0x2d, 0x4e, 0x2a, 0x2e, 0xbd, + 0x05, 0xe9, 0xc1, 0x6c, 0x9e, 0xbf, 0x47, 0x18, + 0x91, 0x77, 0xf7, 0xe8, 0xb6, 0x27, 0x37, 0xa6, + 0x6b, 0x87, 0x29, 0xbb, 0x3b, 0xe5, 0x68, 0x62, + 0x04, 0x3e, 0xad, 0x4d, 0xff, 0xad, 0xf1, 0x22, + 0x87, 0x8d, 0xf6, 0x04, 0x3b, 0x59, 0x22, 0xf7, + 0xfd, 0x88, 0x0e, 0xa4, 0x09, 0xc0, 0x0d, 0x10, + 0x80, 0x10, 0x79, 0xee, 0x70, 0x96, 0xdb, 0x22, + 0x8b, 0xb7, 0xac, 0xe0, 0x98, 0xad, 0xe9, 0xe3, + 0xcb, 0xea, 0x9f, 0xe6, 0x83, 0x28, 0x7c, 0x7e, + 0x4e, 0x9a, 0x8d, 0xd9, 0xf3, 0x86, 0xf4, 0x89, + 0x8b, 0x79, 0x8f, 0xbb, 0xe9, 0x74, 0x02, 0x02, + 0x14, 0x04, 0xea, 0xba, 0x16, 0x10, 0xa1, 0x85, + 0xbe, 0x4e, 0x4e, 0x92, 0xc5, 0x83, 0xf6, 0x1e, + 0x1f, 0xd4, 0x25, 0xc2, 0xc2, 0xb9, 0xce, 0x33, + 0x63, 0x66, 0x79, 0x1f, 0x54, 0x35, 0xc1, 0xe8, + 0x89, 0x34, 0x78, 0x94, 0x36, 0x14, 0xef, 0x01, + 0x1f, 0xf1, 0xbd, 0x77, 0x2c, 0x4d, 0xac, 0x5c, + 0x5c, 0x4a, 0xc6, 0xed, 0xd8, 0x0e, 0x72, 0x84, + 0x83, 0xdc, 0x56, 0x84, 0xc8, 0xf3, 0x89, 0x56, + 0xfd, 0x89, 0xc1, 0xc9, 0x9a, 0x29, 0x91, 0x7e, + 0x19, 0xe9, 0x8b, 0x5b, 0x11, 0x15, 0x4e, 0x6c, + 0xf4, 0x89, 0xe7, 0x6d, 0x68, 0x1e, 0xf9, 0x6c, + 0x23, 0x72, 0x05, 0x68, 0x82, 0x60, 0x84, 0x1f, + 0x83, 0x20, 0x09, 0x86, 0x10, 0x81, 0xec, 0xec, + 0xdc, 0x25, 0x53, 0x20, 0xfa, 0xa9, 0x41, 0x64, + 0xd6, 0x20, 0xf3, 0xf4, 0x52, 0xf2, 0x80, 0x62, + 0x83, 0xc9, 0x23, 0x66, 0x44, 0x95, 0x5a, 0x99, + 0x8a, 0xe1, 0x26, 0x63, 0xc1, 0x8b, 0x31, 0xf9, + 0x21, 0x06, 0x77, 0x04, 0x27, 0xf2, 0x0c, 0x63, + 0x83, 0x45, 0xa0, 0xa9, 0x7b, 0xcf, 0xdf, 0xd7, + 0x56, 0x75, 0xbc, 0xdd, 0x95, 0x36, 0xb1, 0x75, + 0x39, 0x05, 0x00, 0x3c, 0x8a, 0x79, 0xd6, 0xe9, + 0xf0, 0x4b, 0xdc, 0x51, 0x6b, 0x01, 0x94, 0x16, + 0x87, 0x12, 0x92, 0x6c, 0x07, 0xc1, 0xf5, 0x58, + 0xb7, 0x2a, 0x81, 0xf5, 0xa0, 0x37, 0x8b, 0xa6, + 0x22, 0xfe, 0x28, 0x0a, 0x7e, 0x68, 0xe2, 0xda, + 0x6c, 0x53, 0xee, 0x0e, 0x8d, 0x2d, 0x8b, 0x0b, + 0xda, 0xf8, 0x99, 0x3e, 0x0e, 0xed, 0x9f, 0xc1, + 0x2b, 0xf6, 0xfe, 0xe9, 0x52, 0x38, 0x7b, 0x83, + 0x9a, 0x50, 0xa6, 0xd7, 0x49, 0x83, 0x43, 0x7e, + 0x82, 0xec, 0xc7, 0x09, 0x3d, 0x3d, 0xb1, 0xee, + 0xe8, 0xc5, 0x6a, 0xc3, 0x3d, 0x4b, 0x4c, 0x6a, + 0xbb, 0x0b, 0x2c, 0x24, 0x2e, 0xdb, 0x7d, 0x57, + 0x87, 0xb4, 0x80, 0xa5, 0xae, 0xff, 0x54, 0xa8, + 0xa5, 0x27, 0x69, 0x95, 0xc8, 0xe7, 0x79, 0xc7, + 0x89, 0x2a, 0x73, 0x49, 0xcb, 0xf5, 0xc5, 0xbc, + 0x4a, 0xe0, 0x73, 0xa9, 0xbc, 0x88, 0x64, 0x96, + 0x98, 0xa5, 0x1e, 0xe3, 0x43, 0xc1, 0x7d, 0x78, + 0xc7, 0x94, 0x72, 0xd4, 0x2c, 0x6e, 0x85, 0x39, + 0x9a, 0xaf, 0xdb, 0xa1, 0xe9, 0xe2, 0xcb, 0x37, + 0x04, 0xc6, 0x8c, 0x81, 0xd3, 0x2a, 0xb7, 0xbe, + 0x6c, 0x07, 0x1f, 0x5e, 0xd9, 0x00, 0xd2, 0xf7, + 0xe1, 0xa7, 0xbc, 0x0c, 0xb6, 0x6d, 0xfb, 0x3f, + 0x3d, 0x24, 0xaa, 0xfb, 0x7e, 0xe1, 0xb5, 0x1b, + 0xff, 0x38, 0xaa, 0x69, 0x59, 0x38, 0x52, 0x9a, + 0x0e, 0x6d, 0xbc, 0xde, 0x4f, 0x13, 0x09, 0x17, + 0xc4, 0xa9, 0x05, 0x84, 0xbc, 0x50, 0xef, 0x40, + 0xb0, 0x4c, 0x24, 0x32, 0xed, 0x94, 0x2c, 0xdd, + 0xda, 0x20, 0x24, 0x67, 0xe2, 0xea, 0x71, 0x3d, + 0x4a, 0x04, 0x0d, 0x98, 0x29, 0x20, 0x4c, 0xeb, + 0x70, 0xce, 0x45, 0x9e, 0x5a, 0xaf, 0xb6, 0xa3, + 0x92, 0xc8, 0x28, 0xf2, 0xe3, 0xe8, 0x8a, 0x5d, + 0x0a, 0x33, 0x79, 0x9b, 0x6a, 0xf3, 0x30, 0x01, + 0x1d, 0x47, 0xbd, 0x01, 0xcc, 0x4d, 0x71, 0xc0, + 0x56, 0xfa, 0xfd, 0x37, 0xed, 0x0f, 0x27, 0xc0, + 0xbb, 0xa0, 0xee, 0xc3, 0x79, 0x8b, 0xe7, 0x41, + 0x8f, 0xfa, 0x3a, 0xcb, 0x45, 0x3b, 0x85, 0x9f, + 0x06, 0x90, 0xb2, 0x51, 0x7a, 0xc3, 0x11, 0x41, + 0x4b, 0xe3, 0x26, 0x94, 0x3e, 0xa2, 0xfd, 0x0a, + 0xda, 0x50, 0xf6, 0x50, 0x78, 0x19, 0x6c, 0x52, + 0xd1, 0x12, 0x76, 0xc2, 0x50, 0x2f, 0x0b, 0xca, + 0x33, 0xe5, 0x79, 0x93, 0x14, 0x03, 0x01, 0x00, + 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, 0x24, 0x2b, + 0x51, 0x42, 0x95, 0x6b, 0xca, 0x9f, 0x42, 0x5d, + 0xd2, 0xd9, 0x67, 0xf9, 0x49, 0x30, 0xfd, 0x2a, + 0x46, 0xd3, 0x04, 0xf4, 0x86, 0xf9, 0x11, 0x34, + 0x82, 0xac, 0xe2, 0xc2, 0x2d, 0xc4, 0xd0, 0xfe, + 0xa9, 0xc9, 0x4b, 0x17, 0x03, 0x01, 0x00, 0x21, + 0x65, 0x1c, 0xe9, 0x5c, 0xb6, 0xe2, 0x7c, 0x8e, + 0x49, 0x12, 0x1b, 0xe6, 0x40, 0xd3, 0x97, 0x21, + 0x76, 0x01, 0xe5, 0x80, 0x5e, 0xf3, 0x11, 0x47, + 0x25, 0x02, 0x78, 0x8e, 0x6b, 0xae, 0xb3, 0xf3, + 0x59, 0x15, 0x03, 0x01, 0x00, 0x16, 0x38, 0xc1, + 0x99, 0x2e, 0xf8, 0x6f, 0x45, 0xa4, 0x10, 0x79, + 0x5b, 0xc1, 0x47, 0x9a, 0xf6, 0x5c, 0x90, 0xeb, + 0xa6, 0xe3, 0x1a, 0x24, }, }}, } +var tls11ECDHEAESServerScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x01, 0x46, 0x01, 0x00, 0x01, + 0x42, 0x03, 0x03, 0x51, 0x9f, 0xa3, 0xb0, 0xb7, + 0x1d, 0x26, 0x93, 0x36, 0xc0, 0x8d, 0x7e, 0xf8, + 0x4f, 0x6f, 0xc9, 0x3c, 0x31, 0x1e, 0x7f, 0xb1, + 0xf0, 0xc1, 0x0f, 0xf9, 0x0c, 0xa2, 0xd5, 0xca, + 0x48, 0xe5, 0x35, 0x00, 0x00, 0xd0, 0xc0, 0x30, + 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14, + 0xc0, 0x0a, 0xc0, 0x22, 0xc0, 0x21, 0x00, 0xa5, + 0x00, 0xa3, 0x00, 0xa1, 0x00, 0x9f, 0x00, 0x6b, + 0x00, 0x6a, 0x00, 0x69, 0x00, 0x68, 0x00, 0x39, + 0x00, 0x38, 0x00, 0x37, 0x00, 0x36, 0x00, 0x88, + 0x00, 0x87, 0x00, 0x86, 0x00, 0x85, 0xc0, 0x32, + 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f, + 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35, + 0x00, 0x84, 0xc0, 0x12, 0xc0, 0x08, 0xc0, 0x1c, + 0xc0, 0x1b, 0x00, 0x16, 0x00, 0x13, 0x00, 0x10, + 0x00, 0x0d, 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, + 0xc0, 0x2f, 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, + 0xc0, 0x13, 0xc0, 0x09, 0xc0, 0x1f, 0xc0, 0x1e, + 0x00, 0xa4, 0x00, 0xa2, 0x00, 0xa0, 0x00, 0x9e, + 0x00, 0x67, 0x00, 0x40, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x33, 0x00, 0x32, 0x00, 0x31, 0x00, 0x30, + 0x00, 0x9a, 0x00, 0x99, 0x00, 0x98, 0x00, 0x97, + 0x00, 0x45, 0x00, 0x44, 0x00, 0x43, 0x00, 0x42, + 0xc0, 0x31, 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, + 0xc0, 0x0e, 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, + 0x00, 0x2f, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07, + 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x09, 0x00, 0x14, + 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x01, 0x00, + 0x00, 0x49, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, + 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, + 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, + 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, + 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, + 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, + 0x00, 0x11, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x01, 0x01, + }, + { + 0x16, 0x03, 0x02, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x13, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x02, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x02, 0x01, 0x0f, 0x0c, 0x00, 0x01, + 0x0b, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39, + 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27, + 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99, + 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0, + 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46, + 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc, + 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b, + 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c, + 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6, + 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d, + 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28, + 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a, + 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07, + 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0, + 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea, + 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f, + 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79, + 0x90, 0x33, 0x00, 0x80, 0x16, 0x83, 0x9b, 0xf9, + 0x72, 0xdb, 0x9f, 0x55, 0x02, 0xe1, 0x04, 0xf7, + 0xb5, 0x3f, 0x4c, 0x71, 0x13, 0x5a, 0x91, 0xe9, + 0x1d, 0xeb, 0x9d, 0x9c, 0xfb, 0x88, 0xef, 0xca, + 0xec, 0x7d, 0x9b, 0xdd, 0xd9, 0xee, 0x2b, 0x8e, + 0xef, 0xf8, 0xb6, 0xc7, 0x7d, 0xfe, 0xda, 0x7f, + 0x90, 0x2e, 0x53, 0xf1, 0x64, 0x95, 0xfc, 0x66, + 0xfc, 0x87, 0x27, 0xb6, 0x9f, 0xc8, 0x3a, 0x95, + 0x68, 0x17, 0xe1, 0x7d, 0xf1, 0x88, 0xe8, 0x17, + 0x5f, 0x99, 0x90, 0x3f, 0x47, 0x47, 0x81, 0x06, + 0xe2, 0x8e, 0x22, 0x56, 0x8f, 0xc2, 0x14, 0xe5, + 0x62, 0xa7, 0x0d, 0x41, 0x3c, 0xc7, 0x4a, 0x0a, + 0x74, 0x4b, 0xda, 0x00, 0x8e, 0x4f, 0x90, 0xe6, + 0xd7, 0x68, 0xe5, 0x8b, 0xf2, 0x3f, 0x53, 0x1d, + 0x7a, 0xe6, 0xb3, 0xe9, 0x8a, 0xc9, 0x4d, 0x19, + 0xa6, 0xcf, 0xf9, 0xed, 0x5e, 0x26, 0xdc, 0x90, + 0x1c, 0x41, 0xad, 0x7c, 0x16, 0x03, 0x02, 0x00, + 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x02, 0x00, 0x8a, 0x10, 0x00, 0x00, + 0x86, 0x85, 0x04, 0x01, 0x11, 0xf2, 0xa4, 0x2d, + 0x1a, 0x75, 0x6c, 0xbc, 0x2d, 0x91, 0x95, 0x07, + 0xbe, 0xd6, 0x41, 0x7a, 0xbb, 0xc2, 0x7b, 0xa6, + 0x9b, 0xe3, 0xdc, 0x41, 0x7f, 0x1e, 0x2e, 0xcc, + 0x6d, 0xa3, 0x85, 0x53, 0x98, 0x9f, 0x2d, 0xe6, + 0x3c, 0xb9, 0x82, 0xa6, 0x80, 0x53, 0x9b, 0x71, + 0xfd, 0x27, 0xe5, 0xe5, 0xdf, 0x13, 0xba, 0x56, + 0x62, 0x30, 0x4a, 0x57, 0x27, 0xa7, 0xcc, 0x26, + 0x54, 0xe8, 0x65, 0x6e, 0x4d, 0x00, 0xbf, 0x8a, + 0xcc, 0x89, 0x6a, 0x6c, 0x88, 0xda, 0x79, 0x4f, + 0xc5, 0xad, 0x6d, 0x1d, 0x7c, 0x53, 0x7b, 0x1a, + 0x96, 0xf2, 0xf8, 0x30, 0x01, 0x0b, 0xc2, 0xf0, + 0x78, 0x41, 0xf4, 0x0d, 0xe0, 0xbe, 0xb9, 0x36, + 0xe0, 0xb7, 0xee, 0x16, 0xeb, 0x25, 0x67, 0x04, + 0xc0, 0x2e, 0xd8, 0x34, 0x4a, 0x65, 0xa5, 0xf1, + 0x95, 0x75, 0xc7, 0x39, 0xa9, 0x68, 0xa9, 0x53, + 0x93, 0x5b, 0xca, 0x7b, 0x7f, 0xc0, 0x63, 0x14, + 0x03, 0x02, 0x00, 0x01, 0x01, 0x16, 0x03, 0x02, + 0x00, 0x40, 0x01, 0xb1, 0xae, 0x1b, 0x8a, 0x65, + 0xf8, 0x37, 0x50, 0x39, 0x76, 0xef, 0xaa, 0xda, + 0x84, 0xc9, 0x5f, 0x80, 0xdc, 0xfa, 0xe0, 0x46, + 0x5a, 0xc7, 0x77, 0x9d, 0x76, 0x03, 0xa6, 0xd5, + 0x0e, 0xbf, 0x25, 0x30, 0x5c, 0x99, 0x7d, 0xcd, + 0x2b, 0xaa, 0x2e, 0x8c, 0xdd, 0xda, 0xaa, 0xd7, + 0xf1, 0xf6, 0x33, 0x47, 0x51, 0x1e, 0x83, 0xa1, + 0x83, 0x04, 0xd2, 0xb2, 0xc8, 0xbc, 0x11, 0xc5, + 0x1a, 0x87, + }, + { + 0x16, 0x03, 0x02, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xeb, 0x8b, 0xc7, 0xef, 0xba, 0xe8, 0x0f, 0x69, + 0xfe, 0xfb, 0xc3, 0x3d, 0x90, 0x5d, 0xd7, 0xb2, + 0x51, 0x64, 0xac, 0xc3, 0xae, 0x33, 0x03, 0x42, + 0x45, 0x2d, 0xa7, 0x57, 0xbd, 0xa3, 0x85, 0x64, + 0xa6, 0xfe, 0x5c, 0x33, 0x04, 0x93, 0xf2, 0x7c, + 0x06, 0x6d, 0xd7, 0xd7, 0xcf, 0x4a, 0xaf, 0xb2, + 0xdd, 0x06, 0xdc, 0x28, 0x14, 0x59, 0x23, 0x02, + 0xef, 0x97, 0x6a, 0xe8, 0xec, 0xca, 0x10, 0x44, + 0xcd, 0xb8, 0x50, 0x16, 0x46, 0x5a, 0x05, 0xda, + 0x04, 0xb3, 0x0e, 0xe9, 0xf0, 0x74, 0xc5, 0x23, + 0xc2, 0x0e, 0xa1, 0x54, 0x66, 0x7b, 0xe8, 0x14, + 0x03, 0x02, 0x00, 0x01, 0x01, 0x16, 0x03, 0x02, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x6b, 0x43, 0x1c, 0x58, 0xbc, 0x85, + 0xf7, 0xc1, 0x76, 0xbc, 0x72, 0x33, 0x41, 0x6b, + 0xb8, 0xf8, 0xfd, 0x53, 0x21, 0xc2, 0x41, 0x1b, + 0x72, 0x4f, 0xce, 0x97, 0xca, 0x14, 0x23, 0x4d, + 0xbc, 0x44, 0xd6, 0xd7, 0xfc, 0xbc, 0xfd, 0xfd, + 0x5d, 0x33, 0x42, 0x1b, 0x52, 0x40, 0x0a, 0x2b, + 0x6c, 0x98, 0x17, 0x03, 0x02, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, + 0x31, 0xef, 0x03, 0x7d, 0xa5, 0x74, 0x92, 0x24, + 0x34, 0xae, 0x4e, 0xc9, 0xfc, 0x59, 0xcb, 0x64, + 0xf4, 0x45, 0xb1, 0xac, 0x02, 0xf2, 0x87, 0xe7, + 0x2f, 0xfd, 0x01, 0xca, 0x78, 0x02, 0x2e, 0x3a, + 0x38, 0xcd, 0xb1, 0xe0, 0xf2, 0x2e, 0xf6, 0x27, + 0xa0, 0xac, 0x1f, 0x91, 0x43, 0xc2, 0x3d, 0x15, + 0x03, 0x02, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9f, 0x30, 0x24, 0x56, + 0x2c, 0xde, 0xa0, 0xe6, 0x44, 0x35, 0x30, 0x51, + 0xec, 0xd4, 0x69, 0x2d, 0x46, 0x64, 0x04, 0x21, + 0xfe, 0x7c, 0x4d, 0xc5, 0xd0, 0x8c, 0xf9, 0xd2, + 0x3f, 0x88, 0x69, 0xd5, + }, +} + +// $ go test -run TestRunServer -serve -clientauth 1 \ +// -ciphersuites=0xc011 -minversion=0x0303 -maxversion=0x0303 +var tls12ServerScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x01, 0x1e, 0x01, 0x00, 0x01, + 0x1a, 0x03, 0x03, 0x51, 0xe5, 0x76, 0x84, 0x0e, + 0xb9, 0x17, 0xca, 0x08, 0x47, 0xd9, 0xbd, 0xd0, + 0x94, 0xd1, 0x97, 0xca, 0x5b, 0xe7, 0x20, 0xac, + 0x8e, 0xbb, 0xc7, 0x29, 0xe9, 0x26, 0xcf, 0x7d, + 0xb3, 0xdc, 0x99, 0x00, 0x00, 0x82, 0xc0, 0x30, + 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14, + 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b, + 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32, + 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f, + 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35, + 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13, + 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f, + 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13, + 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67, + 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31, + 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e, + 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f, + 0x00, 0x07, 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, + 0xc0, 0x02, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, + 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, + 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04, + 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34, + 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, + 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, + 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, + 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01, + 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, + 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, + 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f, + 0x00, 0x01, 0x01, + }, + { + 0x16, 0x03, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x11, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x03, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x03, 0x01, 0x11, 0x0c, 0x00, 0x01, + 0x0d, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39, + 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27, + 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99, + 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0, + 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46, + 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc, + 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b, + 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c, + 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6, + 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d, + 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28, + 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a, + 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07, + 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0, + 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea, + 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f, + 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79, + 0x90, 0x33, 0x04, 0x01, 0x00, 0x80, 0x4a, 0xf9, + 0xf5, 0x0a, 0x61, 0x37, 0x7e, 0x4e, 0x92, 0xb5, + 0x1c, 0x91, 0x21, 0xb2, 0xb5, 0x17, 0x00, 0xbf, + 0x01, 0x5f, 0x30, 0xec, 0x62, 0x08, 0xd6, 0x9d, + 0x1a, 0x08, 0x05, 0x72, 0x8b, 0xf4, 0x49, 0x85, + 0xa7, 0xbf, 0x3f, 0x75, 0x58, 0x3e, 0x26, 0x82, + 0xc3, 0x28, 0x07, 0xf9, 0x41, 0x7d, 0x03, 0x14, + 0x3b, 0xc3, 0x05, 0x64, 0xff, 0x52, 0xf4, 0x75, + 0x6a, 0x87, 0xcd, 0xdf, 0x93, 0x31, 0x0a, 0x71, + 0x60, 0x17, 0xc6, 0x33, 0xf0, 0x79, 0xb6, 0x7b, + 0xd0, 0x9c, 0xa0, 0x5f, 0x74, 0x14, 0x2c, 0x5a, + 0xb4, 0x3f, 0x39, 0xf5, 0xe4, 0x9f, 0xbe, 0x6d, + 0x21, 0xd2, 0xa9, 0x42, 0xf7, 0xdc, 0xa6, 0x65, + 0xb7, 0x6a, 0x7e, 0x2e, 0x14, 0xd3, 0xf6, 0xf3, + 0x4b, 0x4c, 0x5b, 0x1a, 0x70, 0x7a, 0xbc, 0xb0, + 0x12, 0xf3, 0x6e, 0x0c, 0xcf, 0x43, 0x22, 0xae, + 0x5b, 0xba, 0x00, 0xf8, 0xfd, 0xaf, 0x16, 0x03, + 0x03, 0x00, 0x0f, 0x0d, 0x00, 0x00, 0x0b, 0x02, + 0x01, 0x40, 0x00, 0x04, 0x04, 0x01, 0x04, 0x03, + 0x00, 0x00, 0x16, 0x03, 0x03, 0x00, 0x04, 0x0e, + 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x03, 0x01, 0xfb, 0x0b, 0x00, 0x01, + 0xf7, 0x00, 0x01, 0xf4, 0x00, 0x01, 0xf1, 0x30, + 0x82, 0x01, 0xed, 0x30, 0x82, 0x01, 0x58, 0xa0, + 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x00, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x30, 0x26, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x31, 0x31, 0x32, 0x30, 0x38, 0x30, 0x37, + 0x35, 0x35, 0x31, 0x32, 0x5a, 0x17, 0x0d, 0x31, + 0x32, 0x31, 0x32, 0x30, 0x37, 0x30, 0x38, 0x30, + 0x30, 0x31, 0x32, 0x5a, 0x30, 0x26, 0x31, 0x10, + 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x07, 0x41, 0x63, 0x6d, 0x65, 0x20, 0x43, 0x6f, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x13, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, + 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x81, 0x9c, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x03, 0x81, 0x8c, 0x00, + 0x30, 0x81, 0x88, 0x02, 0x81, 0x80, 0x4e, 0xd0, + 0x7b, 0x31, 0xe3, 0x82, 0x64, 0xd9, 0x59, 0xc0, + 0xc2, 0x87, 0xa4, 0x5e, 0x1e, 0x8b, 0x73, 0x33, + 0xc7, 0x63, 0x53, 0xdf, 0x66, 0x92, 0x06, 0x84, + 0xf6, 0x64, 0xd5, 0x8f, 0xe4, 0x36, 0xa7, 0x1d, + 0x2b, 0xe8, 0xb3, 0x20, 0x36, 0x45, 0x23, 0xb5, + 0xe3, 0x95, 0xae, 0xed, 0xe0, 0xf5, 0x20, 0x9c, + 0x8d, 0x95, 0xdf, 0x7f, 0x5a, 0x12, 0xef, 0x87, + 0xe4, 0x5b, 0x68, 0xe4, 0xe9, 0x0e, 0x74, 0xec, + 0x04, 0x8a, 0x7f, 0xde, 0x93, 0x27, 0xc4, 0x01, + 0x19, 0x7a, 0xbd, 0xf2, 0xdc, 0x3d, 0x14, 0xab, + 0xd0, 0x54, 0xca, 0x21, 0x0c, 0xd0, 0x4d, 0x6e, + 0x87, 0x2e, 0x5c, 0xc5, 0xd2, 0xbb, 0x4d, 0x4b, + 0x4f, 0xce, 0xb6, 0x2c, 0xf7, 0x7e, 0x88, 0xec, + 0x7c, 0xd7, 0x02, 0x91, 0x74, 0xa6, 0x1e, 0x0c, + 0x1a, 0xda, 0xe3, 0x4a, 0x5a, 0x2e, 0xde, 0x13, + 0x9c, 0x4c, 0x40, 0x88, 0x59, 0x93, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x32, 0x30, 0x30, 0x30, + 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, + 0xff, 0x04, 0x04, 0x03, 0x02, 0x00, 0xa0, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x06, + 0x04, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, 0x0f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x08, 0x30, + 0x06, 0x80, 0x04, 0x01, 0x02, 0x03, 0x04, 0x30, + 0x0b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x05, 0x03, 0x81, 0x81, 0x00, + 0x36, 0x1f, 0xb3, 0x7a, 0x0c, 0x75, 0xc9, 0x6e, + 0x37, 0x46, 0x61, 0x2b, 0xd5, 0xbd, 0xc0, 0xa7, + 0x4b, 0xcc, 0x46, 0x9a, 0x81, 0x58, 0x7c, 0x85, + 0x79, 0x29, 0xc8, 0xc8, 0xc6, 0x67, 0xdd, 0x32, + 0x56, 0x45, 0x2b, 0x75, 0xb6, 0xe9, 0x24, 0xa9, + 0x50, 0x9a, 0xbe, 0x1f, 0x5a, 0xfa, 0x1a, 0x15, + 0xd9, 0xcc, 0x55, 0x95, 0x72, 0x16, 0x83, 0xb9, + 0xc2, 0xb6, 0x8f, 0xfd, 0x88, 0x8c, 0x38, 0x84, + 0x1d, 0xab, 0x5d, 0x92, 0x31, 0x13, 0x4f, 0xfd, + 0x83, 0x3b, 0xc6, 0x9d, 0xf1, 0x11, 0x62, 0xb6, + 0x8b, 0xec, 0xab, 0x67, 0xbe, 0xc8, 0x64, 0xb0, + 0x11, 0x50, 0x46, 0x58, 0x17, 0x6b, 0x99, 0x1c, + 0xd3, 0x1d, 0xfc, 0x06, 0xf1, 0x0e, 0xe5, 0x96, + 0xa8, 0x0c, 0xf9, 0x78, 0x20, 0xb7, 0x44, 0x18, + 0x51, 0x8d, 0x10, 0x7e, 0x4f, 0x94, 0x67, 0xdf, + 0xa3, 0x4e, 0x70, 0x73, 0x8e, 0x90, 0x91, 0x85, + 0x16, 0x03, 0x03, 0x00, 0x8a, 0x10, 0x00, 0x00, + 0x86, 0x85, 0x04, 0x01, 0x5d, 0x3a, 0x92, 0x59, + 0x7f, 0x9a, 0x22, 0x36, 0x0e, 0x1b, 0x1d, 0x2a, + 0x05, 0xb7, 0xa4, 0xb6, 0x5d, 0xfc, 0x51, 0x6e, + 0x15, 0xe5, 0x89, 0x7c, 0xe2, 0xfa, 0x87, 0x38, + 0x05, 0x79, 0x15, 0x92, 0xb4, 0x8f, 0x88, 0x8f, + 0x9d, 0x5d, 0xa0, 0xaf, 0xf8, 0xce, 0xf9, 0x6f, + 0x83, 0xf4, 0x08, 0x69, 0xe4, 0x91, 0xc5, 0xed, + 0xb9, 0xc5, 0xa8, 0x1f, 0x4b, 0xec, 0xef, 0x91, + 0xc1, 0xa3, 0x34, 0x24, 0x18, 0x00, 0x2d, 0xcd, + 0xe6, 0x44, 0xef, 0x5a, 0x3e, 0x52, 0x63, 0x5b, + 0x36, 0x1f, 0x7e, 0xce, 0x9e, 0xaa, 0xda, 0x8d, + 0xb5, 0xc9, 0xea, 0xd8, 0x1b, 0xd1, 0x1c, 0x7c, + 0x07, 0xfc, 0x3c, 0x2d, 0x70, 0x1f, 0xf9, 0x4d, + 0xcb, 0xaa, 0xad, 0x07, 0xd5, 0x6d, 0xbd, 0xa6, + 0x61, 0xf3, 0x2f, 0xa3, 0x9c, 0x45, 0x02, 0x4a, + 0xac, 0x6c, 0xb6, 0x37, 0x95, 0xb1, 0x4a, 0xb5, + 0x0a, 0x4e, 0x60, 0x67, 0xd7, 0xe0, 0x04, 0x16, + 0x03, 0x03, 0x00, 0x88, 0x0f, 0x00, 0x00, 0x84, + 0x04, 0x01, 0x00, 0x80, 0x08, 0x83, 0x53, 0xf0, + 0xf8, 0x14, 0xf5, 0xc2, 0xd1, 0x8b, 0xf0, 0xa5, + 0xc1, 0xd8, 0x1a, 0x36, 0x4b, 0x75, 0x77, 0x02, + 0x19, 0xd8, 0x11, 0x3f, 0x5a, 0x36, 0xfc, 0xe9, + 0x2b, 0x4b, 0xf9, 0xfe, 0xda, 0x8a, 0x0f, 0x6e, + 0x3d, 0xd3, 0x52, 0x87, 0xf7, 0x9c, 0x78, 0x39, + 0xa8, 0xf1, 0xd7, 0xf7, 0x4e, 0x35, 0x33, 0xf9, + 0xc5, 0x76, 0xa8, 0x12, 0xc4, 0x91, 0x33, 0x1d, + 0x93, 0x8c, 0xbf, 0xb1, 0x83, 0x00, 0x90, 0xc5, + 0x52, 0x3e, 0xe0, 0x0a, 0xe8, 0x92, 0x75, 0xdf, + 0x54, 0x5f, 0x9f, 0x95, 0x76, 0x62, 0xb5, 0x85, + 0x69, 0xa4, 0x86, 0x85, 0x6c, 0xf3, 0x6b, 0x2a, + 0x72, 0x7b, 0x4d, 0x42, 0x33, 0x67, 0x4a, 0xce, + 0xb5, 0xdb, 0x9b, 0xae, 0xc0, 0xb0, 0x10, 0xeb, + 0x3b, 0xf4, 0xc2, 0x9a, 0x64, 0x47, 0x4c, 0x1e, + 0xa5, 0x91, 0x7f, 0x6d, 0xd1, 0x03, 0xf5, 0x4a, + 0x90, 0x69, 0x18, 0xb1, 0x14, 0x03, 0x03, 0x00, + 0x01, 0x01, 0x16, 0x03, 0x03, 0x00, 0x24, 0x59, + 0xfc, 0x7e, 0xae, 0xb3, 0xbf, 0xab, 0x4d, 0xdb, + 0x4e, 0xab, 0xa9, 0x6d, 0x6b, 0x4c, 0x60, 0xb6, + 0x16, 0xe0, 0xab, 0x7f, 0x52, 0x2d, 0xa1, 0xfc, + 0xe1, 0x80, 0xd2, 0x8a, 0xa1, 0xe5, 0x8f, 0xa1, + 0x70, 0x93, 0x23, + }, + { + 0x16, 0x03, 0x03, 0x02, 0x67, 0x04, 0x00, 0x02, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xea, 0x8b, 0xc5, 0xef, 0xba, 0x64, 0xb7, 0x23, + 0x08, 0x86, 0x4f, 0x37, 0xe0, 0x8f, 0xbd, 0x75, + 0x71, 0x2b, 0xcb, 0x20, 0x75, 0x11, 0x3b, 0xa2, + 0x9e, 0x39, 0x3c, 0x03, 0xef, 0x6e, 0x41, 0xd7, + 0xcf, 0x1a, 0x2c, 0xf2, 0xfe, 0xc2, 0xd3, 0x65, + 0x59, 0x00, 0x9d, 0x03, 0xb4, 0xf2, 0x20, 0xe4, + 0x33, 0x80, 0xcd, 0xf6, 0xe4, 0x59, 0x22, 0xf7, + 0xfd, 0x88, 0x0e, 0xa4, 0x09, 0xc0, 0x0d, 0x10, + 0x80, 0x10, 0x79, 0xee, 0x70, 0x96, 0xdb, 0x22, + 0x8b, 0xb7, 0xac, 0xe0, 0x98, 0xad, 0xe9, 0xe3, + 0xcb, 0xea, 0x9f, 0xe6, 0x83, 0x28, 0x7c, 0x7e, + 0x4e, 0x9a, 0x8d, 0xd9, 0xf3, 0x86, 0xf4, 0x89, + 0x8b, 0x79, 0x8f, 0xbb, 0xe9, 0x74, 0x02, 0x02, + 0x14, 0x04, 0xea, 0xba, 0x16, 0x10, 0xa1, 0x85, + 0xbe, 0x4e, 0x4e, 0x92, 0xc5, 0x83, 0xf6, 0x1e, + 0x1f, 0xd4, 0x25, 0xc2, 0xc2, 0xb9, 0xce, 0x33, + 0x63, 0x66, 0x79, 0x1f, 0x54, 0x35, 0xc1, 0xe8, + 0x89, 0x34, 0x78, 0x94, 0x36, 0x14, 0xef, 0x01, + 0x1f, 0xf1, 0xbd, 0x77, 0x2c, 0x4d, 0xac, 0x5c, + 0x5c, 0x4a, 0xc6, 0xed, 0xd8, 0x0e, 0x72, 0x84, + 0x83, 0xdc, 0x56, 0x84, 0xc8, 0xf3, 0x89, 0x56, + 0xfd, 0x89, 0xc1, 0xc9, 0x9a, 0x29, 0x91, 0x7e, + 0x19, 0xe9, 0x8b, 0x5b, 0x11, 0x15, 0x4e, 0x6c, + 0xf4, 0x89, 0xe7, 0x6d, 0x68, 0x1e, 0xf9, 0x6c, + 0x23, 0x72, 0x05, 0x68, 0x82, 0x60, 0x84, 0x1f, + 0x83, 0x20, 0x09, 0x86, 0x10, 0x81, 0xec, 0xec, + 0xdc, 0x25, 0x53, 0x20, 0xfa, 0xa9, 0x41, 0x64, + 0xd6, 0x20, 0xf3, 0xf4, 0x52, 0xf2, 0x80, 0x62, + 0x83, 0xc9, 0x23, 0x66, 0x44, 0x95, 0x5a, 0x99, + 0x8a, 0xe1, 0x26, 0x63, 0xc1, 0x8b, 0x31, 0xf9, + 0x21, 0x06, 0x77, 0x04, 0x27, 0xf2, 0x0c, 0x63, + 0x83, 0x45, 0xa0, 0xa9, 0x7b, 0xcf, 0xdf, 0xd7, + 0x56, 0x75, 0xbc, 0xdd, 0x95, 0x36, 0xb1, 0x75, + 0x39, 0x05, 0x00, 0x3c, 0x8a, 0x79, 0xd6, 0xe9, + 0xf0, 0x4b, 0xdc, 0x51, 0x6b, 0x01, 0x94, 0x16, + 0x87, 0x12, 0x92, 0x6c, 0x07, 0xc1, 0xf5, 0x58, + 0xb7, 0x2a, 0x81, 0xf5, 0xa0, 0x37, 0x8b, 0xa6, + 0x22, 0xfe, 0x28, 0x0a, 0x7e, 0x68, 0xe2, 0xda, + 0x6c, 0x53, 0xee, 0x0e, 0x8d, 0x2d, 0x8b, 0x0b, + 0xda, 0xf8, 0x99, 0x3e, 0x0e, 0xed, 0x9f, 0xc1, + 0x2b, 0xf6, 0xfe, 0xe9, 0x52, 0x38, 0x7b, 0x83, + 0x9a, 0x50, 0xa6, 0xd7, 0x49, 0x83, 0x43, 0x7e, + 0x82, 0xec, 0xc7, 0x09, 0x3d, 0x3d, 0xb1, 0xee, + 0xe8, 0xc5, 0x6a, 0xc3, 0x3d, 0x4b, 0x4c, 0x6a, + 0xbb, 0x0b, 0x2c, 0x24, 0x2e, 0xdb, 0x7d, 0x57, + 0x87, 0xb4, 0x80, 0xa5, 0xae, 0xff, 0x54, 0xa8, + 0xa5, 0x27, 0x69, 0x95, 0xc8, 0xe7, 0x79, 0xc7, + 0x89, 0x2a, 0x73, 0x49, 0xcb, 0xf5, 0xc5, 0xbc, + 0x4a, 0xe0, 0x73, 0xa9, 0xbc, 0x88, 0x64, 0x96, + 0x98, 0xa5, 0x1e, 0xe3, 0x43, 0xc1, 0x7d, 0x78, + 0xc7, 0x94, 0x72, 0xd4, 0x2c, 0x6e, 0x85, 0x39, + 0x9a, 0xaf, 0xdb, 0xa1, 0xe9, 0xe2, 0xcb, 0x37, + 0x04, 0xc6, 0x8c, 0x81, 0xd3, 0x2a, 0xb7, 0xbe, + 0x6c, 0x07, 0x1f, 0x5e, 0xd9, 0x00, 0xd2, 0xf7, + 0xe1, 0xa7, 0xbc, 0x0c, 0xb6, 0x6d, 0xfb, 0x3f, + 0x3d, 0x24, 0xaa, 0xfb, 0x7e, 0xe1, 0xb5, 0x1b, + 0xff, 0x38, 0xaa, 0x69, 0x59, 0x38, 0x52, 0x9a, + 0x0e, 0x6d, 0xbc, 0xde, 0x4f, 0x13, 0x09, 0x17, + 0xc4, 0xa9, 0x05, 0x84, 0xbc, 0x50, 0xef, 0x40, + 0xb0, 0x4c, 0x24, 0x32, 0xed, 0x94, 0x2c, 0xdd, + 0xda, 0x20, 0x24, 0x67, 0xe2, 0xea, 0x71, 0x3d, + 0x4a, 0x04, 0x0d, 0x98, 0x29, 0x20, 0x4c, 0xeb, + 0x70, 0xce, 0x45, 0x9e, 0x5a, 0xaf, 0xb6, 0xa3, + 0x92, 0xc8, 0x28, 0xf2, 0xe3, 0xe8, 0x8a, 0x5d, + 0x0a, 0x33, 0x79, 0x9b, 0x6a, 0xf3, 0x30, 0x01, + 0x1d, 0x47, 0xbd, 0x01, 0xcc, 0x4d, 0x71, 0xc0, + 0x56, 0xfa, 0xfd, 0x37, 0xed, 0x0f, 0x27, 0xc0, + 0xbb, 0xa0, 0xee, 0xc3, 0x79, 0x8b, 0xe7, 0x41, + 0x8f, 0xfa, 0x3a, 0xcb, 0x45, 0x3b, 0x85, 0x9f, + 0x06, 0x90, 0xb2, 0x51, 0xc0, 0x48, 0x10, 0xac, + 0x2a, 0xec, 0xec, 0x48, 0x7a, 0x19, 0x47, 0xc4, + 0x2a, 0xeb, 0xb3, 0xa2, 0x07, 0x22, 0x32, 0x78, + 0xf4, 0x73, 0x5e, 0x92, 0x42, 0x15, 0xa1, 0x90, + 0x91, 0xd0, 0xeb, 0x12, 0x14, 0x03, 0x03, 0x00, + 0x01, 0x01, 0x16, 0x03, 0x03, 0x00, 0x24, 0x45, + 0x4b, 0x80, 0x42, 0x46, 0xde, 0xbb, 0xe7, 0x76, + 0xd1, 0x33, 0x92, 0xfc, 0x46, 0x17, 0x6d, 0x21, + 0xf6, 0x0e, 0x16, 0xca, 0x9b, 0x9b, 0x04, 0x65, + 0x16, 0x40, 0x44, 0x64, 0xbc, 0x58, 0xfa, 0x2a, + 0x49, 0xe9, 0xed, 0x17, 0x03, 0x03, 0x00, 0x21, + 0x89, 0x71, 0xcd, 0x56, 0x54, 0xbf, 0x73, 0xde, + 0xfb, 0x4b, 0x4e, 0xf1, 0x7f, 0xc6, 0x75, 0xa6, + 0xbd, 0x6b, 0x6c, 0xd9, 0xdc, 0x0c, 0x71, 0xb4, + 0xb9, 0xbb, 0x6e, 0xfa, 0x9e, 0xc7, 0xc7, 0x4c, + 0x24, 0x15, 0x03, 0x03, 0x00, 0x16, 0x62, 0xea, + 0x65, 0x69, 0x68, 0x4a, 0xce, 0xa7, 0x9e, 0xce, + 0xc0, 0xf1, 0x5c, 0x96, 0xd9, 0x1f, 0x49, 0xac, + 0x2d, 0x05, 0x89, 0x94, + }, +} + // cert.pem and key.pem were generated with generate_cert.go // Thus, they have no ExtKeyUsage fields and trigger an error // when verification is turned on. @@ -1999,3 +3167,630 @@ qTdQRYlHRftgnWK1AkANibn9PRYJ7mJyJ9Dyj2QeNcSkSTzrt0tPvUMf4+meJymN 1Ntu5+S1DLLzfxlaljWG6ylW6DNxujCyuXIV2rvA -----END RSA PRIVATE KEY----- */ + +var clientECDSACertificate = loadPEMCert(` +-----BEGIN CERTIFICATE----- +MIIB/DCCAV4CCQCaMIRsJjXZFzAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw +EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0 +eSBMdGQwHhcNMTIxMTE0MTMyNTUzWhcNMjIxMTEyMTMyNTUzWjBBMQswCQYDVQQG +EwJBVTEMMAoGA1UECBMDTlNXMRAwDgYDVQQHEwdQeXJtb250MRIwEAYDVQQDEwlK +b2VsIFNpbmcwgZswEAYHKoZIzj0CAQYFK4EEACMDgYYABACVjJF1FMBexFe01MNv +ja5oHt1vzobhfm6ySD6B5U7ixohLZNz1MLvT/2XMW/TdtWo+PtAd3kfDdq0Z9kUs +jLzYHQFMH3CQRnZIi4+DzEpcj0B22uCJ7B0rxE4wdihBsmKo+1vx+U56jb0JuK7q +ixgnTy5w/hOWusPTQBbNZU6sER7m8TAJBgcqhkjOPQQBA4GMADCBiAJCAOAUxGBg +C3JosDJdYUoCdFzCgbkWqD8pyDbHgf9stlvZcPE4O1BIKJTLCRpS8V3ujfK58PDa +2RU6+b0DeoeiIzXsAkIBo9SKeDUcSpoj0gq+KxAxnZxfvuiRs9oa9V2jI/Umi0Vw +jWVim34BmT0Y9hCaOGGbLlfk+syxis7iI6CH8OFnUes= +-----END CERTIFICATE----- +`) + +/* corresponding key for cert is: +-----BEGIN EC PARAMETERS----- +BgUrgQQAIw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBkJN9X4IqZIguiEVKMqeBUP5xtRsEv4HJEtOpOGLELwO53SD78Ew8 +k+wLWoqizS3NpQyMtrU8JFdWfj+C57UNkOugBwYFK4EEACOhgYkDgYYABACVjJF1 +FMBexFe01MNvja5oHt1vzobhfm6ySD6B5U7ixohLZNz1MLvT/2XMW/TdtWo+PtAd +3kfDdq0Z9kUsjLzYHQFMH3CQRnZIi4+DzEpcj0B22uCJ7B0rxE4wdihBsmKo+1vx ++U56jb0JuK7qixgnTy5w/hOWusPTQBbNZU6sER7m8Q== +-----END EC PRIVATE KEY----- +*/ +var clientauthECDSATests = []clientauthTest{ + // Server asks for cert with empty CA list, client gives one + // go test -run "TestRunServer" -serve \ + // -clientauth 1 -ciphersuites=0xc00a + // openssl s_client -host 127.0.0.1 -port 10443 \ + // -cipher ECDHE-ECDSA-AES256-SHA -key client.key -cert client.crt + {"RequestClientCert, client gives it", RequestClientCert, []*x509.Certificate{clientECDSACertificate}, [][]byte{ + { + 0x16, 0x03, 0x01, 0x00, 0xa0, 0x01, 0x00, 0x00, + 0x9c, 0x03, 0x03, 0x51, 0xe5, 0x73, 0xc5, 0xae, + 0x51, 0x94, 0xb4, 0xf2, 0xe8, 0xf6, 0x03, 0x0e, + 0x3b, 0x34, 0xaf, 0xf0, 0xdc, 0x1b, 0xcc, 0xd8, + 0x0c, 0x45, 0x82, 0xd4, 0xd6, 0x76, 0x04, 0x6e, + 0x4f, 0x7a, 0x24, 0x00, 0x00, 0x04, 0xc0, 0x0a, + 0x00, 0xff, 0x01, 0x00, 0x00, 0x6f, 0x00, 0x0b, + 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, + 0x00, 0x34, 0x00, 0x32, 0x00, 0x0e, 0x00, 0x0d, + 0x00, 0x19, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x18, + 0x00, 0x09, 0x00, 0x0a, 0x00, 0x16, 0x00, 0x17, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x07, 0x00, 0x14, + 0x00, 0x15, 0x00, 0x04, 0x00, 0x05, 0x00, 0x12, + 0x00, 0x13, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, + 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x22, 0x00, 0x20, + 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05, 0x01, + 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, + 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, + 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, + 0x00, 0x0f, 0x00, 0x01, 0x01, + }, + { + 0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0a, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x02, 0x0e, 0x0b, 0x00, 0x02, 0x0a, 0x00, 0x02, + 0x07, 0x00, 0x02, 0x04, 0x30, 0x82, 0x02, 0x00, + 0x30, 0x82, 0x01, 0x62, 0x02, 0x09, 0x00, 0xb8, + 0xbf, 0x2d, 0x47, 0xa0, 0xd2, 0xeb, 0xf4, 0x30, + 0x09, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x04, 0x01, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x32, 0x31, + 0x31, 0x32, 0x32, 0x31, 0x35, 0x30, 0x36, 0x33, + 0x32, 0x5a, 0x17, 0x0d, 0x32, 0x32, 0x31, 0x31, + 0x32, 0x30, 0x31, 0x35, 0x30, 0x36, 0x33, 0x32, + 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, + 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, + 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, + 0x30, 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, + 0x2b, 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86, + 0x00, 0x04, 0x00, 0xc4, 0xa1, 0xed, 0xbe, 0x98, + 0xf9, 0x0b, 0x48, 0x73, 0x36, 0x7e, 0xc3, 0x16, + 0x56, 0x11, 0x22, 0xf2, 0x3d, 0x53, 0xc3, 0x3b, + 0x4d, 0x21, 0x3d, 0xcd, 0x6b, 0x75, 0xe6, 0xf6, + 0xb0, 0xdc, 0x9a, 0xdf, 0x26, 0xc1, 0xbc, 0xb2, + 0x87, 0xf0, 0x72, 0x32, 0x7c, 0xb3, 0x64, 0x2f, + 0x1c, 0x90, 0xbc, 0xea, 0x68, 0x23, 0x10, 0x7e, + 0xfe, 0xe3, 0x25, 0xc0, 0x48, 0x3a, 0x69, 0xe0, + 0x28, 0x6d, 0xd3, 0x37, 0x00, 0xef, 0x04, 0x62, + 0xdd, 0x0d, 0xa0, 0x9c, 0x70, 0x62, 0x83, 0xd8, + 0x81, 0xd3, 0x64, 0x31, 0xaa, 0x9e, 0x97, 0x31, + 0xbd, 0x96, 0xb0, 0x68, 0xc0, 0x9b, 0x23, 0xde, + 0x76, 0x64, 0x3f, 0x1a, 0x5c, 0x7f, 0xe9, 0x12, + 0x0e, 0x58, 0x58, 0xb6, 0x5f, 0x70, 0xdd, 0x9b, + 0xd8, 0xea, 0xd5, 0xd7, 0xf5, 0xd5, 0xcc, 0xb9, + 0xb6, 0x9f, 0x30, 0x66, 0x5b, 0x66, 0x9a, 0x20, + 0xe2, 0x27, 0xe5, 0xbf, 0xfe, 0x3b, 0x30, 0x09, + 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, + 0x01, 0x03, 0x81, 0x8c, 0x00, 0x30, 0x81, 0x88, + 0x02, 0x42, 0x01, 0x88, 0xa2, 0x4f, 0xeb, 0xe2, + 0x45, 0xc5, 0x48, 0x7d, 0x1b, 0xac, 0xf5, 0xed, + 0x98, 0x9d, 0xae, 0x47, 0x70, 0xc0, 0x5e, 0x1b, + 0xb6, 0x2f, 0xbd, 0xf1, 0xb6, 0x4d, 0xb7, 0x61, + 0x40, 0xd3, 0x11, 0xa2, 0xce, 0xee, 0x0b, 0x7e, + 0x92, 0x7e, 0xff, 0x76, 0x9d, 0xc3, 0x3b, 0x7e, + 0xa5, 0x3f, 0xce, 0xfa, 0x10, 0xe2, 0x59, 0xec, + 0x47, 0x2d, 0x7c, 0xac, 0xda, 0x4e, 0x97, 0x0e, + 0x15, 0xa0, 0x6f, 0xd0, 0x02, 0x42, 0x01, 0x4d, + 0xfc, 0xbe, 0x67, 0x13, 0x9c, 0x2d, 0x05, 0x0e, + 0xbd, 0x3f, 0xa3, 0x8c, 0x25, 0xc1, 0x33, 0x13, + 0x83, 0x0d, 0x94, 0x06, 0xbb, 0xd4, 0x37, 0x7a, + 0xf6, 0xec, 0x7a, 0xc9, 0x86, 0x2e, 0xdd, 0xd7, + 0x11, 0x69, 0x7f, 0x85, 0x7c, 0x56, 0xde, 0xfb, + 0x31, 0x78, 0x2b, 0xe4, 0xc7, 0x78, 0x0d, 0xae, + 0xcb, 0xbe, 0x9e, 0x4e, 0x36, 0x24, 0x31, 0x7b, + 0x6a, 0x0f, 0x39, 0x95, 0x12, 0x07, 0x8f, 0x2a, + 0x16, 0x03, 0x01, 0x01, 0x1a, 0x0c, 0x00, 0x01, + 0x16, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39, + 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27, + 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99, + 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0, + 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46, + 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc, + 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b, + 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c, + 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6, + 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d, + 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28, + 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a, + 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07, + 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0, + 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea, + 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f, + 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79, + 0x90, 0x33, 0x00, 0x8b, 0x30, 0x81, 0x88, 0x02, + 0x42, 0x00, 0xc6, 0x85, 0x8e, 0x06, 0xb7, 0x04, + 0x04, 0xe9, 0xcd, 0x9e, 0x3e, 0xcb, 0x66, 0x23, + 0x95, 0xb4, 0x42, 0x9c, 0x64, 0x81, 0x39, 0x05, + 0x3f, 0xb5, 0x21, 0xf8, 0x28, 0xaf, 0x60, 0x6b, + 0x4d, 0x3d, 0xba, 0xa1, 0x4b, 0x5e, 0x77, 0xef, + 0xe7, 0x59, 0x28, 0xfe, 0x1d, 0xc1, 0x27, 0xa2, + 0xff, 0xa8, 0xde, 0x33, 0x48, 0xb3, 0xc1, 0x85, + 0x6a, 0x42, 0x9b, 0xf9, 0x7e, 0x7e, 0x31, 0xc2, + 0xe5, 0xbd, 0x66, 0x02, 0x42, 0x00, 0xad, 0x7d, + 0x06, 0x35, 0xab, 0xec, 0x8d, 0xac, 0xd4, 0xba, + 0x1b, 0x49, 0x5e, 0x05, 0x5f, 0xf0, 0x97, 0x93, + 0x82, 0xb8, 0x2b, 0x8d, 0x91, 0x98, 0x63, 0x8e, + 0xb4, 0x14, 0x62, 0xdb, 0x1e, 0xc9, 0x2b, 0x30, + 0xf8, 0x41, 0x9b, 0xa6, 0xe6, 0xbc, 0xde, 0x0e, + 0x68, 0x30, 0x21, 0xf4, 0xa8, 0xa9, 0x1b, 0xec, + 0x44, 0x4f, 0x5d, 0x02, 0x2f, 0x60, 0x45, 0x60, + 0xba, 0xe0, 0x4e, 0xc0, 0xd4, 0x3b, 0x01, 0x16, + 0x03, 0x01, 0x00, 0x09, 0x0d, 0x00, 0x00, 0x05, + 0x02, 0x01, 0x40, 0x00, 0x00, 0x16, 0x03, 0x01, + 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x01, 0x02, 0x0a, 0x0b, 0x00, 0x02, + 0x06, 0x00, 0x02, 0x03, 0x00, 0x02, 0x00, 0x30, + 0x82, 0x01, 0xfc, 0x30, 0x82, 0x01, 0x5e, 0x02, + 0x09, 0x00, 0x9a, 0x30, 0x84, 0x6c, 0x26, 0x35, + 0xd9, 0x17, 0x30, 0x09, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x32, 0x31, 0x31, 0x31, 0x34, 0x31, 0x33, + 0x32, 0x35, 0x35, 0x33, 0x5a, 0x17, 0x0d, 0x32, + 0x32, 0x31, 0x31, 0x31, 0x32, 0x31, 0x33, 0x32, + 0x35, 0x35, 0x33, 0x5a, 0x30, 0x41, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x41, 0x55, 0x31, 0x0c, 0x30, 0x0a, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x13, 0x03, 0x4e, 0x53, + 0x57, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x07, 0x13, 0x07, 0x50, 0x79, 0x72, 0x6d, + 0x6f, 0x6e, 0x74, 0x31, 0x12, 0x30, 0x10, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x4a, 0x6f, + 0x65, 0x6c, 0x20, 0x53, 0x69, 0x6e, 0x67, 0x30, + 0x81, 0x9b, 0x30, 0x10, 0x06, 0x07, 0x2a, 0x86, + 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, + 0x81, 0x04, 0x00, 0x23, 0x03, 0x81, 0x86, 0x00, + 0x04, 0x00, 0x95, 0x8c, 0x91, 0x75, 0x14, 0xc0, + 0x5e, 0xc4, 0x57, 0xb4, 0xd4, 0xc3, 0x6f, 0x8d, + 0xae, 0x68, 0x1e, 0xdd, 0x6f, 0xce, 0x86, 0xe1, + 0x7e, 0x6e, 0xb2, 0x48, 0x3e, 0x81, 0xe5, 0x4e, + 0xe2, 0xc6, 0x88, 0x4b, 0x64, 0xdc, 0xf5, 0x30, + 0xbb, 0xd3, 0xff, 0x65, 0xcc, 0x5b, 0xf4, 0xdd, + 0xb5, 0x6a, 0x3e, 0x3e, 0xd0, 0x1d, 0xde, 0x47, + 0xc3, 0x76, 0xad, 0x19, 0xf6, 0x45, 0x2c, 0x8c, + 0xbc, 0xd8, 0x1d, 0x01, 0x4c, 0x1f, 0x70, 0x90, + 0x46, 0x76, 0x48, 0x8b, 0x8f, 0x83, 0xcc, 0x4a, + 0x5c, 0x8f, 0x40, 0x76, 0xda, 0xe0, 0x89, 0xec, + 0x1d, 0x2b, 0xc4, 0x4e, 0x30, 0x76, 0x28, 0x41, + 0xb2, 0x62, 0xa8, 0xfb, 0x5b, 0xf1, 0xf9, 0x4e, + 0x7a, 0x8d, 0xbd, 0x09, 0xb8, 0xae, 0xea, 0x8b, + 0x18, 0x27, 0x4f, 0x2e, 0x70, 0xfe, 0x13, 0x96, + 0xba, 0xc3, 0xd3, 0x40, 0x16, 0xcd, 0x65, 0x4e, + 0xac, 0x11, 0x1e, 0xe6, 0xf1, 0x30, 0x09, 0x06, + 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, + 0x03, 0x81, 0x8c, 0x00, 0x30, 0x81, 0x88, 0x02, + 0x42, 0x00, 0xe0, 0x14, 0xc4, 0x60, 0x60, 0x0b, + 0x72, 0x68, 0xb0, 0x32, 0x5d, 0x61, 0x4a, 0x02, + 0x74, 0x5c, 0xc2, 0x81, 0xb9, 0x16, 0xa8, 0x3f, + 0x29, 0xc8, 0x36, 0xc7, 0x81, 0xff, 0x6c, 0xb6, + 0x5b, 0xd9, 0x70, 0xf1, 0x38, 0x3b, 0x50, 0x48, + 0x28, 0x94, 0xcb, 0x09, 0x1a, 0x52, 0xf1, 0x5d, + 0xee, 0x8d, 0xf2, 0xb9, 0xf0, 0xf0, 0xda, 0xd9, + 0x15, 0x3a, 0xf9, 0xbd, 0x03, 0x7a, 0x87, 0xa2, + 0x23, 0x35, 0xec, 0x02, 0x42, 0x01, 0xa3, 0xd4, + 0x8a, 0x78, 0x35, 0x1c, 0x4a, 0x9a, 0x23, 0xd2, + 0x0a, 0xbe, 0x2b, 0x10, 0x31, 0x9d, 0x9c, 0x5f, + 0xbe, 0xe8, 0x91, 0xb3, 0xda, 0x1a, 0xf5, 0x5d, + 0xa3, 0x23, 0xf5, 0x26, 0x8b, 0x45, 0x70, 0x8d, + 0x65, 0x62, 0x9b, 0x7e, 0x01, 0x99, 0x3d, 0x18, + 0xf6, 0x10, 0x9a, 0x38, 0x61, 0x9b, 0x2e, 0x57, + 0xe4, 0xfa, 0xcc, 0xb1, 0x8a, 0xce, 0xe2, 0x23, + 0xa0, 0x87, 0xf0, 0xe1, 0x67, 0x51, 0xeb, 0x16, + 0x03, 0x01, 0x00, 0x8a, 0x10, 0x00, 0x00, 0x86, + 0x85, 0x04, 0x00, 0xcd, 0x1c, 0xe8, 0x66, 0x5b, + 0xa8, 0x9d, 0x83, 0x2f, 0x7e, 0x1d, 0x0b, 0x59, + 0x23, 0xbc, 0x30, 0xcf, 0xa3, 0xaf, 0x21, 0xdc, + 0xf2, 0x57, 0x49, 0x56, 0x30, 0x25, 0x7c, 0x84, + 0x5d, 0xad, 0xaa, 0x9c, 0x7b, 0x2a, 0x95, 0x58, + 0x3d, 0x30, 0x87, 0x01, 0x3b, 0xb7, 0xea, 0xcb, + 0xc4, 0xa3, 0xeb, 0x22, 0xbf, 0x2d, 0x61, 0x17, + 0x8c, 0x9b, 0xe8, 0x1b, 0xb2, 0x87, 0x16, 0x78, + 0xd5, 0xfd, 0x8b, 0xdd, 0x00, 0x0f, 0xda, 0x8e, + 0xfd, 0x28, 0x36, 0xeb, 0xe4, 0xc5, 0x42, 0x14, + 0xc7, 0xbd, 0x29, 0x5e, 0x9a, 0xed, 0x5e, 0xc1, + 0xf7, 0xf4, 0xbd, 0xbd, 0x15, 0x9c, 0xe8, 0x44, + 0x71, 0xa7, 0xb6, 0xe9, 0xfa, 0x7e, 0x97, 0xcb, + 0x96, 0x3e, 0x53, 0x76, 0xfb, 0x11, 0x1f, 0x36, + 0x8f, 0x30, 0xfb, 0x71, 0x3a, 0x75, 0x3a, 0x25, + 0x7b, 0xa2, 0xc1, 0xf9, 0x3e, 0x58, 0x5f, 0x07, + 0x16, 0xed, 0xe1, 0xf7, 0xc1, 0xb1, 0x16, 0x03, + 0x01, 0x00, 0x90, 0x0f, 0x00, 0x00, 0x8c, 0x00, + 0x8a, 0x30, 0x81, 0x87, 0x02, 0x42, 0x00, 0xb2, + 0xd3, 0x91, 0xe6, 0xd5, 0x9b, 0xb2, 0xb8, 0x03, + 0xf4, 0x85, 0x4d, 0x43, 0x79, 0x1f, 0xb6, 0x6f, + 0x0c, 0xcd, 0x67, 0x5f, 0x5e, 0xca, 0xee, 0xb3, + 0xe4, 0xab, 0x1e, 0x58, 0xc3, 0x04, 0xa9, 0x8a, + 0xa7, 0xcf, 0xaa, 0x33, 0x88, 0xd5, 0x35, 0xd2, + 0x80, 0x8f, 0xfa, 0x1b, 0x3c, 0x3d, 0xf7, 0x80, + 0x50, 0xde, 0x80, 0x30, 0x64, 0xee, 0xc0, 0xb3, + 0x91, 0x6e, 0x5d, 0x1e, 0xc0, 0xdc, 0x3a, 0x93, + 0x02, 0x41, 0x4e, 0xca, 0x98, 0x41, 0x8c, 0x36, + 0xf2, 0x12, 0xbf, 0x8e, 0x0f, 0x69, 0x8e, 0xf8, + 0x7b, 0x9d, 0xba, 0x9c, 0x5c, 0x48, 0x79, 0xf4, + 0xba, 0x3d, 0x06, 0xa5, 0xab, 0x47, 0xe0, 0x1a, + 0x45, 0x28, 0x3a, 0x8f, 0xbf, 0x14, 0x24, 0x36, + 0xd1, 0x1d, 0x29, 0xdc, 0xde, 0x72, 0x5b, 0x76, + 0x41, 0x67, 0xe8, 0xe5, 0x71, 0x4a, 0x77, 0xe9, + 0xed, 0x02, 0x19, 0xdd, 0xe4, 0xaa, 0xe9, 0x2d, + 0xe7, 0x47, 0x32, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xfa, 0xc3, + 0xf2, 0x35, 0xd0, 0x6d, 0x32, 0x78, 0x6a, 0xd6, + 0xe6, 0x70, 0x5e, 0x00, 0x4c, 0x35, 0xf1, 0xe0, + 0x21, 0xcf, 0xc3, 0x78, 0xcd, 0xe0, 0x2b, 0x0b, + 0xf4, 0xeb, 0xf9, 0xc0, 0x38, 0xf2, 0x9a, 0x31, + 0x55, 0x07, 0x2b, 0x8d, 0x68, 0x40, 0x31, 0x08, + 0xaa, 0xe3, 0x16, 0xcf, 0x4b, 0xd4, + }, + { + 0x16, 0x03, 0x01, 0x02, 0x76, 0x04, 0x00, 0x02, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x02, 0x6c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xe8, 0x8b, 0xde, 0xef, 0xba, 0xf9, 0xdb, 0x95, + 0x24, 0xa5, 0x49, 0xb3, 0x23, 0xd8, 0x73, 0x88, + 0x50, 0x42, 0xed, 0xeb, 0xa3, 0xd8, 0xab, 0x31, + 0x9c, 0xd0, 0x00, 0x01, 0xef, 0xc0, 0xbf, 0xab, + 0x59, 0x55, 0xb5, 0xb9, 0xef, 0xa5, 0xa6, 0xec, + 0x69, 0xed, 0x00, 0x2f, 0x47, 0xdb, 0x75, 0x52, + 0x0c, 0xe5, 0x86, 0xb7, 0x02, 0x59, 0x22, 0xf7, + 0xfd, 0x8b, 0xff, 0xa4, 0x09, 0xc0, 0x1c, 0x10, + 0x80, 0x10, 0x7f, 0x4c, 0x7a, 0x94, 0x40, 0x10, + 0x0d, 0xda, 0x8a, 0xe5, 0x4a, 0xbc, 0xd0, 0xc0, + 0x4b, 0xa5, 0x33, 0x97, 0xc6, 0xe7, 0x40, 0x7f, + 0x7f, 0x8c, 0xf9, 0xf8, 0xc8, 0xb8, 0xfb, 0x8c, + 0xdd, 0x28, 0x81, 0xae, 0xfd, 0x37, 0x20, 0x3a, + 0x40, 0x37, 0x99, 0xc4, 0x21, 0x01, 0xc4, 0x91, + 0xb0, 0x5e, 0x11, 0xc5, 0xa9, 0xfd, 0x9a, 0x02, + 0x7e, 0x97, 0x6a, 0x86, 0x89, 0xb8, 0xc1, 0x32, + 0x4c, 0x7e, 0x6d, 0x47, 0x61, 0x0e, 0xe3, 0xc2, + 0xf0, 0x62, 0x3c, 0xc6, 0x71, 0x4f, 0xbb, 0x47, + 0x65, 0xb1, 0xd9, 0x22, 0x79, 0x15, 0xea, 0x1f, + 0x4b, 0x2a, 0x8a, 0xa4, 0xc8, 0x73, 0x34, 0xba, + 0x83, 0xe4, 0x70, 0x99, 0xc9, 0xcf, 0xbe, 0x64, + 0x99, 0xb9, 0xfa, 0xe9, 0xaf, 0x5d, 0xc7, 0x20, + 0x26, 0xde, 0xc5, 0x06, 0x12, 0x36, 0x4f, 0x4d, + 0xc0, 0xbb, 0x81, 0x5b, 0x5e, 0x38, 0xc3, 0x07, + 0x21, 0x04, 0x1a, 0x53, 0x9c, 0x59, 0xac, 0x2d, + 0xe6, 0xa5, 0x93, 0xa5, 0x19, 0xc6, 0xb0, 0xf7, + 0x56, 0x5d, 0xdf, 0xd1, 0xf4, 0xfd, 0x44, 0x6d, + 0xc6, 0xa2, 0x31, 0xa7, 0x35, 0x42, 0x18, 0x50, + 0x0c, 0x4f, 0x6e, 0xe3, 0x3b, 0xa3, 0xaa, 0x1c, + 0xbe, 0x41, 0x0d, 0xce, 0x6c, 0x62, 0xe1, 0x96, + 0x2d, 0xbd, 0x14, 0x31, 0xe3, 0xc4, 0x5b, 0xbf, + 0xf6, 0xde, 0xec, 0x42, 0xe8, 0xc7, 0x2a, 0x0b, + 0xdb, 0x2d, 0x7c, 0xf0, 0x3f, 0x45, 0x32, 0x45, + 0x09, 0x47, 0x09, 0x0f, 0x21, 0x22, 0x45, 0x06, + 0x11, 0xb8, 0xf9, 0xe6, 0x67, 0x90, 0x4b, 0x4a, + 0xde, 0x81, 0xfb, 0xeb, 0xe7, 0x9a, 0x08, 0x30, + 0xcf, 0x51, 0xe1, 0xd9, 0xfa, 0x79, 0xa3, 0xcc, + 0x65, 0x1a, 0x83, 0x86, 0xc9, 0x7a, 0x41, 0xf5, + 0xdf, 0xa0, 0x7c, 0x44, 0x23, 0x17, 0xf3, 0x62, + 0xe8, 0xa9, 0x31, 0x1e, 0x6b, 0x05, 0x4b, 0x4f, + 0x9d, 0x91, 0x46, 0x92, 0xa6, 0x25, 0x32, 0xca, + 0xa1, 0x75, 0xda, 0xe6, 0x80, 0x3e, 0x7f, 0xd1, + 0x26, 0x57, 0x07, 0x42, 0xe4, 0x91, 0xff, 0xbd, + 0x44, 0xae, 0x98, 0x5c, 0x1d, 0xdf, 0x11, 0xe3, + 0xae, 0x87, 0x5e, 0xb7, 0x69, 0xad, 0x34, 0x7f, + 0x3a, 0x07, 0x7c, 0xdf, 0xfc, 0x76, 0x17, 0x8b, + 0x62, 0xc8, 0xe1, 0x78, 0x2a, 0xc8, 0xb9, 0x8a, + 0xbb, 0x5c, 0xfb, 0x38, 0x74, 0x91, 0x6e, 0x12, + 0x0c, 0x1f, 0x8e, 0xe1, 0xc2, 0x01, 0xd8, 0x9d, + 0x23, 0x0f, 0xc4, 0x67, 0x5d, 0xe5, 0x67, 0x4b, + 0x94, 0x6e, 0x69, 0x72, 0x90, 0x2d, 0x52, 0x78, + 0x8e, 0x61, 0xba, 0xdf, 0x4e, 0xf5, 0xdc, 0xfb, + 0x73, 0xbe, 0x03, 0x70, 0xd9, 0x01, 0x30, 0xf3, + 0xa1, 0xbb, 0x9a, 0x5f, 0xec, 0x9e, 0xed, 0x8d, + 0xdd, 0x53, 0xfd, 0x60, 0xc3, 0x2b, 0x7a, 0x00, + 0x2c, 0xf9, 0x0a, 0x57, 0x47, 0x45, 0x43, 0xb3, + 0x23, 0x01, 0x9c, 0xee, 0x54, 0x4d, 0x58, 0xd3, + 0x71, 0x1c, 0xc9, 0xd3, 0x30, 0x9e, 0x14, 0xa5, + 0xf3, 0xbf, 0x4d, 0x9b, 0xb7, 0x13, 0x21, 0xae, + 0xd2, 0x8d, 0x6e, 0x6f, 0x1c, 0xcc, 0xb2, 0x41, + 0xb2, 0x64, 0x56, 0x83, 0xce, 0xd1, 0x0c, 0x79, + 0x32, 0x78, 0xef, 0xc5, 0x21, 0xb1, 0xe8, 0xc4, + 0x42, 0xa7, 0x8d, 0xc1, 0xfa, 0xa1, 0x9c, 0x3c, + 0x21, 0xd8, 0xe9, 0x90, 0xe2, 0x7c, 0x14, 0x26, + 0xfe, 0x61, 0x3e, 0xf9, 0x71, 0x1d, 0x5d, 0x49, + 0x3b, 0xb1, 0xb8, 0x42, 0xa1, 0xb8, 0x1c, 0x75, + 0x7d, 0xee, 0xed, 0xfc, 0xe6, 0x20, 0x2b, 0x9e, + 0x10, 0x52, 0xda, 0x56, 0x4d, 0x64, 0x6c, 0x41, + 0xc1, 0xf7, 0x60, 0x0c, 0x10, 0x65, 0x6f, 0xd4, + 0xe9, 0x9b, 0x0d, 0x83, 0x13, 0xc8, 0x5a, 0xa3, + 0x56, 0x2a, 0x42, 0xc6, 0x1c, 0xfe, 0xdb, 0xba, + 0x3d, 0x04, 0x12, 0xfd, 0x28, 0xeb, 0x78, 0xdd, + 0xbc, 0xc8, 0x0d, 0xa1, 0xce, 0xd4, 0x54, 0xbf, + 0xaf, 0xe1, 0x60, 0x0c, 0xa3, 0xc3, 0xc3, 0x62, + 0x58, 0xc1, 0x79, 0xa7, 0x95, 0x41, 0x09, 0x24, + 0xc6, 0x9a, 0x50, 0x14, 0x03, 0x01, 0x00, 0x01, + 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x4d, 0x7b, + 0x5f, 0x28, 0x5e, 0x68, 0x6c, 0xa3, 0x65, 0xc7, + 0x7e, 0x49, 0x6c, 0xb3, 0x67, 0xbb, 0xd0, 0x75, + 0xa2, 0x9e, 0x8c, 0x92, 0x4f, 0x8c, 0x33, 0x14, + 0x7c, 0x6c, 0xf1, 0x74, 0x97, 0xc3, 0xe0, 0x10, + 0xe9, 0x0d, 0xc2, 0x30, 0x5c, 0x23, 0xee, 0x1d, + 0x16, 0x2e, 0xb9, 0x96, 0x2b, 0x2d, 0x17, 0x03, + 0x01, 0x00, 0x20, 0xf2, 0xc8, 0xa7, 0x1b, 0x60, + 0x46, 0xee, 0xe5, 0x7e, 0xc9, 0x35, 0xb3, 0xf1, + 0x7c, 0x32, 0x0c, 0x85, 0x94, 0x59, 0x57, 0x27, + 0xb0, 0xbd, 0x52, 0x86, 0x90, 0xf1, 0xb7, 0x4d, + 0x1e, 0xc1, 0x16, 0x17, 0x03, 0x01, 0x00, 0x30, + 0xff, 0x85, 0x50, 0xdf, 0x3f, 0xfc, 0xa2, 0x61, + 0x1a, 0x12, 0xc0, 0x1e, 0x10, 0x32, 0x88, 0x50, + 0xa0, 0x2c, 0x80, 0xda, 0x77, 0xea, 0x09, 0x47, + 0xe0, 0x85, 0x07, 0x29, 0x45, 0x65, 0x19, 0xa3, + 0x8d, 0x99, 0xb8, 0xbf, 0xb6, 0xbc, 0x76, 0xe2, + 0x50, 0x24, 0x82, 0x0a, 0xfd, 0xdd, 0x35, 0x09, + 0x15, 0x03, 0x01, 0x00, 0x20, 0xe7, 0x36, 0xf6, + 0x61, 0xd2, 0x95, 0x3c, 0xb6, 0x65, 0x7b, 0xb2, + 0xb8, 0xdf, 0x03, 0x53, 0xeb, 0xf7, 0x16, 0xe0, + 0xe0, 0x15, 0x22, 0x71, 0x70, 0x62, 0x73, 0xad, + 0xb5, 0x1a, 0x77, 0x44, 0x57, + }, + }}, +} + +var aesGCMServerScript = [][]byte{ + { + 0x16, 0x03, 0x01, 0x01, 0x1c, 0x01, 0x00, 0x01, + 0x18, 0x03, 0x03, 0x52, 0x1e, 0x74, 0xf0, 0xb0, + 0xc1, 0x8b, 0x16, 0xf9, 0x74, 0xfc, 0x16, 0xc4, + 0x11, 0x18, 0x96, 0x08, 0x25, 0x38, 0x4f, 0x98, + 0x98, 0xbe, 0xb5, 0x61, 0xdf, 0x94, 0x15, 0xcc, + 0x9b, 0x61, 0xef, 0x00, 0x00, 0x80, 0xc0, 0x30, + 0xc0, 0x2c, 0xc0, 0x28, 0xc0, 0x24, 0xc0, 0x14, + 0xc0, 0x0a, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x6b, + 0x00, 0x6a, 0x00, 0x39, 0x00, 0x38, 0xc0, 0x32, + 0xc0, 0x2e, 0xc0, 0x2a, 0xc0, 0x26, 0xc0, 0x0f, + 0xc0, 0x05, 0x00, 0x9d, 0x00, 0x3d, 0x00, 0x35, + 0xc0, 0x12, 0xc0, 0x08, 0x00, 0x16, 0x00, 0x13, + 0xc0, 0x0d, 0xc0, 0x03, 0x00, 0x0a, 0xc0, 0x2f, + 0xc0, 0x2b, 0xc0, 0x27, 0xc0, 0x23, 0xc0, 0x13, + 0xc0, 0x09, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x67, + 0x00, 0x40, 0x00, 0x33, 0x00, 0x32, 0xc0, 0x31, + 0xc0, 0x2d, 0xc0, 0x29, 0xc0, 0x25, 0xc0, 0x0e, + 0xc0, 0x04, 0x00, 0x9c, 0x00, 0x3c, 0x00, 0x2f, + 0xc0, 0x11, 0xc0, 0x07, 0xc0, 0x0c, 0xc0, 0x02, + 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, + 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08, + 0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x01, 0x00, + 0x00, 0x6f, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, + 0x01, 0x02, 0x00, 0x0a, 0x00, 0x34, 0x00, 0x32, + 0x00, 0x0e, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x0b, + 0x00, 0x0c, 0x00, 0x18, 0x00, 0x09, 0x00, 0x0a, + 0x00, 0x16, 0x00, 0x17, 0x00, 0x08, 0x00, 0x06, + 0x00, 0x07, 0x00, 0x14, 0x00, 0x15, 0x00, 0x04, + 0x00, 0x05, 0x00, 0x12, 0x00, 0x13, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x03, 0x00, 0x0f, 0x00, 0x10, + 0x00, 0x11, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x22, 0x00, 0x20, 0x06, 0x01, 0x06, 0x02, + 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, + 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x03, 0x01, + 0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x02, 0x03, 0x01, 0x01, 0x00, 0x0f, 0x00, 0x01, + 0x01, + }, + { + 0x16, 0x03, 0x03, 0x00, 0x30, 0x02, 0x00, 0x00, + 0x2c, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc0, 0x2f, 0x00, 0x00, + 0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x03, + 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02, + 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0, + 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4, + 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, + 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, + 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, + 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, + 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, + 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, + 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, + 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30, + 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, + 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33, + 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, + 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, + 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, + 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, + 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79, + 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10, + 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43, + 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85, + 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c, + 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5, + 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c, + 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56, + 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26, + 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21, + 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf, + 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07, + 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39, + 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3, + 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf, + 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb, + 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03, + 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, + 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85, + 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23, + 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39, + 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2, + 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, + 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, + 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, + 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, + 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, + 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85, + 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, + 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, + 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59, + 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7, + 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95, + 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66, + 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3, + 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13, + 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba, + 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31, + 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50, + 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f, + 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96, + 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f, + 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b, + 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70, + 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e, + 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9, + 0x16, 0x03, 0x03, 0x01, 0x11, 0x0c, 0x00, 0x01, + 0x0d, 0x03, 0x00, 0x19, 0x85, 0x04, 0x01, 0x39, + 0xdc, 0xee, 0x44, 0x17, 0x5e, 0xdb, 0xd7, 0x27, + 0xaf, 0xb6, 0x56, 0xd9, 0xb4, 0x43, 0x5a, 0x99, + 0xcf, 0xaa, 0x31, 0x37, 0x0c, 0x6f, 0x3a, 0xa0, + 0xf8, 0x53, 0xc4, 0x74, 0xd1, 0x91, 0x0a, 0x46, + 0xf5, 0x38, 0x3b, 0x5c, 0x09, 0xd8, 0x97, 0xdc, + 0x4b, 0xaa, 0x70, 0x26, 0x48, 0xf2, 0xd6, 0x0b, + 0x31, 0xc9, 0xf8, 0xd4, 0x98, 0x43, 0xe1, 0x6c, + 0xd5, 0xc7, 0xb2, 0x8e, 0x0b, 0x01, 0xe6, 0xb6, + 0x00, 0x28, 0x80, 0x7b, 0xfc, 0x96, 0x8f, 0x0d, + 0xa2, 0x4f, 0xb0, 0x79, 0xaf, 0xdc, 0x61, 0x28, + 0x63, 0x33, 0x78, 0xf6, 0x31, 0x39, 0xfd, 0x8a, + 0xf4, 0x15, 0x18, 0x11, 0xfe, 0xdb, 0xd5, 0x07, + 0xda, 0x2c, 0xed, 0x49, 0xa0, 0x23, 0xbf, 0xd0, + 0x3a, 0x38, 0x1d, 0x54, 0xae, 0x1c, 0x7b, 0xea, + 0x29, 0xee, 0xd0, 0x38, 0xc1, 0x76, 0xa7, 0x7f, + 0x2a, 0xf4, 0xce, 0x1e, 0xac, 0xcc, 0x94, 0x79, + 0x90, 0x33, 0x04, 0x01, 0x00, 0x80, 0x0d, 0x8e, + 0x79, 0xe6, 0x86, 0xf6, 0xb6, 0xfb, 0x6b, 0x6a, + 0xcc, 0x55, 0xe4, 0x80, 0x4d, 0xc5, 0x0c, 0xc6, + 0xa3, 0x9f, 0x1d, 0x39, 0xd2, 0x98, 0x57, 0x31, + 0xa2, 0x90, 0x73, 0xe8, 0xd2, 0xcd, 0xb0, 0x93, + 0x1a, 0x60, 0x0f, 0x38, 0x02, 0x3b, 0x1b, 0x25, + 0x56, 0xec, 0x44, 0xab, 0xbe, 0x2e, 0x0c, 0xc0, + 0x6e, 0x54, 0x91, 0x50, 0xd6, 0xb1, 0xa2, 0x98, + 0x14, 0xa8, 0x35, 0x62, 0x9d, 0xca, 0xfb, 0x0f, + 0x64, 0x2b, 0x05, 0xa0, 0xa0, 0x57, 0xef, 0xcd, + 0x95, 0x45, 0x13, 0x5a, 0x9b, 0x3d, 0xdb, 0x42, + 0x54, 0x7f, 0xb9, 0x17, 0x08, 0x7f, 0xb2, 0xf0, + 0xb1, 0xc3, 0xdf, 0x67, 0x95, 0xe2, 0x73, 0xf2, + 0x76, 0xa3, 0x97, 0xfd, 0x9c, 0x92, 0x4a, 0xdb, + 0x95, 0x1e, 0x91, 0x95, 0xae, 0x3d, 0xae, 0x58, + 0xb5, 0x03, 0x6f, 0x5c, 0x3a, 0x19, 0xab, 0x92, + 0xa5, 0x09, 0x6b, 0x40, 0x61, 0xb0, 0x16, 0x03, + 0x03, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, + }, + { + 0x16, 0x03, 0x03, 0x00, 0x8a, 0x10, 0x00, 0x00, + 0x86, 0x85, 0x04, 0x01, 0xba, 0xb8, 0xad, 0x69, + 0x20, 0x5e, 0xc1, 0x61, 0xc3, 0x0f, 0xb4, 0x30, + 0x64, 0x66, 0x70, 0x96, 0x33, 0x3c, 0x8e, 0x12, + 0x56, 0xbf, 0x6d, 0xb8, 0x6d, 0xc6, 0xba, 0xea, + 0xfc, 0x38, 0xc0, 0x8b, 0x87, 0xa8, 0xf3, 0x87, + 0xa1, 0xd5, 0xb6, 0xb0, 0x72, 0xc7, 0xd4, 0x19, + 0x56, 0xa0, 0x91, 0xe1, 0x45, 0xc7, 0xf1, 0x7d, + 0xb0, 0x1d, 0x78, 0x18, 0xf6, 0x3d, 0xbf, 0x1a, + 0x23, 0x93, 0x0b, 0x19, 0xb1, 0x00, 0x56, 0xc9, + 0x5e, 0x89, 0xd4, 0x9d, 0xd9, 0x5b, 0xe0, 0xb8, + 0xff, 0x2f, 0x7d, 0x93, 0xae, 0x5b, 0xa5, 0x1f, + 0x1f, 0x2b, 0x09, 0xe5, 0xf6, 0x07, 0x26, 0xa3, + 0xed, 0xcb, 0x6a, 0x1a, 0xd6, 0x14, 0x83, 0x9b, + 0xd3, 0x9d, 0x47, 0x1b, 0xf3, 0x72, 0x5f, 0x69, + 0x21, 0x8f, 0xfa, 0x09, 0x38, 0x1a, 0x6b, 0x91, + 0xcf, 0x19, 0x32, 0x54, 0x58, 0x8e, 0xee, 0xaf, + 0xeb, 0x06, 0x9b, 0x3a, 0x34, 0x16, 0x66, 0x14, + 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, 0x03, + 0x00, 0x28, 0xc6, 0x96, 0x67, 0x62, 0xcc, 0x47, + 0x01, 0xb5, 0xbd, 0xb7, 0x24, 0xd3, 0xb6, 0xfd, + 0xb8, 0x46, 0xce, 0x82, 0x6d, 0x31, 0x1f, 0x15, + 0x11, 0x8f, 0xed, 0x62, 0x71, 0x5f, 0xae, 0xb6, + 0xa9, 0x0c, 0x24, 0x1d, 0xe8, 0x26, 0x51, 0xca, + 0x7c, 0x42, + }, + { + 0x16, 0x03, 0x03, 0x00, 0x72, 0x04, 0x00, 0x00, + 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, + 0xea, 0x8b, 0xfb, 0xef, 0xba, 0xc8, 0x88, 0x94, + 0x44, 0x99, 0x5f, 0x02, 0x68, 0x3a, 0x12, 0x67, + 0x7f, 0xb9, 0x39, 0x71, 0x84, 0xe0, 0x30, 0xe6, + 0x90, 0x6c, 0xcf, 0x32, 0x29, 0x29, 0x5c, 0x5a, + 0x8b, 0x7d, 0xaa, 0x11, 0x28, 0x26, 0xb5, 0xce, + 0xd2, 0x88, 0xd5, 0xb0, 0x5f, 0x94, 0x37, 0xa2, + 0x48, 0xd9, 0x53, 0xb2, 0xab, 0x59, 0x23, 0x3d, + 0x81, 0x6e, 0x64, 0x89, 0xca, 0x1a, 0x84, 0x16, + 0xdf, 0x31, 0x10, 0xde, 0x52, 0x7f, 0x50, 0xf3, + 0xd9, 0x27, 0xa0, 0xe8, 0x34, 0x15, 0x9e, 0x11, + 0xdd, 0xba, 0xce, 0x40, 0x17, 0xf3, 0x67, 0x14, + 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, 0x03, + 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x35, 0xcb, 0x17, 0x66, 0xee, 0xfd, + 0x27, 0xdb, 0xb8, 0xa8, 0x8a, 0xf1, 0x56, 0x67, + 0x89, 0x0d, 0x13, 0xac, 0xe2, 0x31, 0xb9, 0xa2, + 0x26, 0xbb, 0x1c, 0xcf, 0xd1, 0xb2, 0x48, 0x1d, + 0x0d, 0xb1, 0x17, 0x03, 0x03, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc0, + 0x89, 0x7c, 0x58, 0x6a, 0x9b, 0x00, 0x05, 0x8c, + 0x7f, 0x28, 0x54, 0x61, 0x44, 0x10, 0xee, 0x85, + 0x26, 0xa8, 0x04, 0xcd, 0xca, 0x85, 0x60, 0xf2, + 0xeb, 0x22, 0xbd, 0x9e, 0x15, 0x03, 0x03, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x10, 0xe4, 0xe5, 0xf9, 0x85, 0xe3, 0xb0, + 0xec, 0x84, 0x29, 0x91, 0x05, 0x7d, 0x86, 0xe3, + 0x97, 0xeb, 0xb2, + }, +} diff --git a/libgo/go/crypto/tls/key_agreement.go b/libgo/go/crypto/tls/key_agreement.go index b6e73fe..7e820c1 100644 --- a/libgo/go/crypto/tls/key_agreement.go +++ b/libgo/go/crypto/tls/key_agreement.go @@ -6,11 +6,14 @@ package tls import ( "crypto" + "crypto/ecdsa" "crypto/elliptic" "crypto/md5" "crypto/rsa" "crypto/sha1" + "crypto/sha256" "crypto/x509" + "encoding/asn1" "errors" "io" "math/big" @@ -36,7 +39,7 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certifi } ciphertext := ckx.ciphertext - if version != versionSSL30 { + if version != VersionSSL30 { ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1]) if ciphertextLen != len(ckx.ciphertext)-2 { return nil, errors.New("bad ClientKeyExchange") @@ -82,34 +85,94 @@ func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello return preMasterSecret, ckx, nil } +// sha1Hash calculates a SHA1 hash over the given byte slices. +func sha1Hash(slices [][]byte) []byte { + hsha1 := sha1.New() + for _, slice := range slices { + hsha1.Write(slice) + } + return hsha1.Sum(nil) +} + // md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the // concatenation of an MD5 and SHA1 hash. -func md5SHA1Hash(slices ...[]byte) []byte { +func md5SHA1Hash(slices [][]byte) []byte { md5sha1 := make([]byte, md5.Size+sha1.Size) hmd5 := md5.New() for _, slice := range slices { hmd5.Write(slice) } copy(md5sha1, hmd5.Sum(nil)) + copy(md5sha1[md5.Size:], sha1Hash(slices)) + return md5sha1 +} - hsha1 := sha1.New() +// sha256Hash implements TLS 1.2's hash function. +func sha256Hash(slices [][]byte) []byte { + h := sha256.New() for _, slice := range slices { - hsha1.Write(slice) + h.Write(slice) } - copy(md5sha1[md5.Size:], hsha1.Sum(nil)) - return md5sha1 + return h.Sum(nil) +} + +// hashForServerKeyExchange hashes the given slices and returns their digest +// and the identifier of the hash function used. The hashFunc argument is only +// used for >= TLS 1.2 and precisely identifies the hash function to use. +func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ...[]byte) ([]byte, crypto.Hash, error) { + if version >= VersionTLS12 { + switch hashFunc { + case hashSHA256: + return sha256Hash(slices), crypto.SHA256, nil + case hashSHA1: + return sha1Hash(slices), crypto.SHA1, nil + default: + return nil, crypto.Hash(0), errors.New("tls: unknown hash function used by peer") + } + } + if sigType == signatureECDSA { + return sha1Hash(slices), crypto.SHA1, nil + } + return md5SHA1Hash(slices), crypto.MD5SHA1, nil +} + +// 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. +func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) { + if len(clientSignatureAndHashes) == 0 { + // If the client didn't specify any signature_algorithms + // extension then we can assume that it supports SHA1. See + // http://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + return hashSHA1, nil + } + + for _, sigAndHash := range clientSignatureAndHashes { + if sigAndHash.signature != sigType { + continue + } + switch sigAndHash.hash { + case hashSHA1, hashSHA256: + return sigAndHash.hash, nil + } + } + + return 0, errors.New("tls: client doesn't support any common hash functions") } // 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. -type ecdheRSAKeyAgreement struct { +// pre-master secret is then calculated using ECDH. The signature may +// either be ECDSA or RSA. +type ecdheKeyAgreement struct { + version uint16 + sigType uint8 privateKey []byte curve elliptic.Curve x, y *big.Int } -func (ka *ecdheRSAKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { +func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { var curveid uint16 Curve: @@ -150,16 +213,55 @@ Curve: serverECDHParams[3] = byte(len(ecdhePublic)) copy(serverECDHParams[4:], ecdhePublic) - md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams) - sig, err := rsa.SignPKCS1v15(config.rand(), cert.PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1) + var tls12HashId uint8 + if ka.version >= VersionTLS12 { + if tls12HashId, err = pickTLS12HashForSignature(ka.sigType, clientHello.signatureAndHashes); err != nil { + return nil, err + } + } + + digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, hello.random, serverECDHParams) if err != nil { - return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + return nil, err + } + var sig []byte + switch ka.sigType { + case signatureECDSA: + privKey, ok := cert.PrivateKey.(*ecdsa.PrivateKey) + if !ok { + return nil, errors.New("ECDHE ECDSA requires an ECDSA server private key") + } + r, s, err := ecdsa.Sign(config.rand(), privKey, digest) + if err != nil { + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + } + sig, err = asn1.Marshal(ecdsaSignature{r, s}) + case signatureRSA: + privKey, ok := cert.PrivateKey.(*rsa.PrivateKey) + if !ok { + return nil, errors.New("ECDHE RSA requires a RSA server private key") + } + sig, err = rsa.SignPKCS1v15(config.rand(), privKey, hashFunc, digest) + if err != nil { + return nil, errors.New("failed to sign ECDHE parameters: " + err.Error()) + } + default: + return nil, errors.New("unknown ECDHE signature algorithm") } skx := new(serverKeyExchangeMsg) - skx.key = make([]byte, len(serverECDHParams)+2+len(sig)) + sigAndHashLen := 0 + if ka.version >= VersionTLS12 { + sigAndHashLen = 2 + } + skx.key = make([]byte, len(serverECDHParams)+sigAndHashLen+2+len(sig)) copy(skx.key, serverECDHParams) k := skx.key[len(serverECDHParams):] + if ka.version >= VersionTLS12 { + k[0] = tls12HashId + k[1] = ka.sigType + k = k[2:] + } k[0] = byte(len(sig) >> 8) k[1] = byte(len(sig)) copy(k[2:], sig) @@ -167,7 +269,7 @@ Curve: return skx, nil } -func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { +func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { return nil, errors.New("bad ClientKeyExchange") } @@ -185,7 +287,7 @@ func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, cert *C var errServerKeyExchange = errors.New("invalid ServerKeyExchange") -func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { +func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { if len(skx.key) < 4 { return errServerKeyExchange } @@ -219,17 +321,62 @@ func (ka *ecdheRSAKeyAgreement) processServerKeyExchange(config *Config, clientH if len(sig) < 2 { return errServerKeyExchange } + + var tls12HashId uint8 + if ka.version >= VersionTLS12 { + // handle SignatureAndHashAlgorithm + var sigAndHash []uint8 + sigAndHash, sig = sig[:2], sig[2:] + if sigAndHash[1] != ka.sigType { + return errServerKeyExchange + } + tls12HashId = sigAndHash[0] + if len(sig) < 2 { + return errServerKeyExchange + } + } sigLen := int(sig[0])<<8 | int(sig[1]) if sigLen+2 != len(sig) { return errServerKeyExchange } sig = sig[2:] - md5sha1 := md5SHA1Hash(clientHello.random, serverHello.random, serverECDHParams) - return rsa.VerifyPKCS1v15(cert.PublicKey.(*rsa.PublicKey), crypto.MD5SHA1, md5sha1, sig) + digest, hashFunc, err := hashForServerKeyExchange(ka.sigType, tls12HashId, ka.version, clientHello.random, serverHello.random, serverECDHParams) + if err != nil { + return err + } + switch ka.sigType { + case signatureECDSA: + pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey) + if !ok { + return errors.New("ECDHE ECDSA requires a ECDSA server public key") + } + ecdsaSig := new(ecdsaSignature) + if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("ECDSA signature contained zero or negative values") + } + if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) { + return errors.New("ECDSA verification failure") + } + case signatureRSA: + pubKey, ok := cert.PublicKey.(*rsa.PublicKey) + if !ok { + return errors.New("ECDHE RSA requires a RSA server public key") + } + if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil { + return err + } + default: + return errors.New("unknown ECDHE signature algorithm") + } + + return nil } -func (ka *ecdheRSAKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { +func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { if ka.curve == nil { return nil, nil, errors.New("missing ServerKeyExchange message") } diff --git a/libgo/go/crypto/tls/prf.go b/libgo/go/crypto/tls/prf.go index df1eaad..fb8b3ab 100644 --- a/libgo/go/crypto/tls/prf.go +++ b/libgo/go/crypto/tls/prf.go @@ -5,9 +5,11 @@ package tls import ( + "crypto" "crypto/hmac" "crypto/md5" "crypto/sha1" + "crypto/sha256" "hash" ) @@ -43,8 +45,8 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { } } -// pRF10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5. -func pRF10(result, secret, label, seed []byte) { +// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, section 5. +func prf10(result, secret, label, seed []byte) { hashSHA1 := sha1.New hashMD5 := md5.New @@ -62,9 +64,18 @@ func pRF10(result, secret, label, seed []byte) { } } -// pRF30 implements the SSL 3.0 pseudo-random function, as defined in +// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, section 5. +func prf12(result, secret, label, seed []byte) { + labelAndSeed := make([]byte, len(label)+len(seed)) + copy(labelAndSeed, label) + copy(labelAndSeed[len(label):], seed) + + pHash(result, secret, labelAndSeed, sha256.New) +} + +// prf30 implements the SSL 3.0 pseudo-random function, as defined in // www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6. -func pRF30(result, secret, label, seed []byte) { +func prf30(result, secret, label, seed []byte) { hashSHA1 := sha1.New() hashMD5 := md5.New() @@ -106,19 +117,27 @@ var keyExpansionLabel = []byte("key expansion") var clientFinishedLabel = []byte("client finished") var serverFinishedLabel = []byte("server finished") +func prfForVersion(version uint16) func(result, secret, label, seed []byte) { + switch version { + case VersionSSL30: + return prf30 + case VersionTLS10, VersionTLS11: + return prf10 + case VersionTLS12: + return prf12 + default: + panic("unknown version") + } +} + // masterFromPreMasterSecret generates the master secret from the pre-master // secret. See http://tools.ietf.org/html/rfc5246#section-8.1 func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte) []byte { - prf := pRF10 - if version == versionSSL30 { - prf = pRF30 - } - var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], clientRandom) copy(seed[len(clientRandom):], serverRandom) masterSecret := make([]byte, masterSecretLength) - prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) + prfForVersion(version)(masterSecret, preMasterSecret, masterSecretLabel, seed[0:]) return masterSecret } @@ -126,18 +145,13 @@ func masterFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, se // secret, given the lengths of the MAC key, cipher key and IV, as defined in // RFC 2246, section 6.3. func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) { - prf := pRF10 - if version == versionSSL30 { - prf = pRF30 - } - var seed [tlsRandomLength * 2]byte copy(seed[0:len(clientRandom)], serverRandom) copy(seed[len(serverRandom):], clientRandom) n := 2*macLen + 2*keyLen + 2*ivLen keyMaterial := make([]byte, n) - prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) + prfForVersion(version)(keyMaterial, masterSecret, keyExpansionLabel, seed[0:]) clientMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] serverMAC = keyMaterial[:macLen] @@ -153,37 +167,34 @@ func keysFromMasterSecret(version uint16, masterSecret, clientRandom, serverRand } func newFinishedHash(version uint16) finishedHash { - return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version} + if version >= VersionTLS12 { + return finishedHash{sha256.New(), sha256.New(), nil, nil, version} + } + return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), version} } // A finishedHash calculates the hash of a set of handshake messages suitable // for including in a Finished message. type finishedHash struct { - clientMD5 hash.Hash - clientSHA1 hash.Hash - serverMD5 hash.Hash - serverSHA1 hash.Hash - version uint16 + client hash.Hash + server hash.Hash + + // Prior to TLS 1.2, an additional MD5 hash is required. + clientMD5 hash.Hash + serverMD5 hash.Hash + + version uint16 } func (h finishedHash) Write(msg []byte) (n int, err error) { - h.clientMD5.Write(msg) - h.clientSHA1.Write(msg) - h.serverMD5.Write(msg) - h.serverSHA1.Write(msg) - return len(msg), nil -} + h.client.Write(msg) + h.server.Write(msg) -// finishedSum10 calculates the contents of the verify_data member of a TLSv1 -// Finished message given the MD5 and SHA1 hashes of a set of handshake -// messages. -func finishedSum10(md5, sha1, label, masterSecret []byte) []byte { - seed := make([]byte, len(md5)+len(sha1)) - copy(seed, md5) - copy(seed[len(md5):], sha1) - out := make([]byte, finishedVerifyLength) - pRF10(out, masterSecret, label, seed) - return out + if h.version < VersionTLS12 { + h.clientMD5.Write(msg) + h.serverMD5.Write(msg) + } + return len(msg), nil } // finishedSum30 calculates the contents of the verify_data member of a SSLv3 @@ -224,23 +235,57 @@ var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52} // clientSum returns the contents of the verify_data member of a client's // Finished message. func (h finishedHash) clientSum(masterSecret []byte) []byte { - if h.version == versionSSL30 { - return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic) + if h.version == VersionSSL30 { + return finishedSum30(h.clientMD5, h.client, masterSecret, ssl3ClientFinishedMagic) } - md5 := h.clientMD5.Sum(nil) - sha1 := h.clientSHA1.Sum(nil) - return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret) + out := make([]byte, finishedVerifyLength) + if h.version >= VersionTLS12 { + seed := h.client.Sum(nil) + prf12(out, masterSecret, clientFinishedLabel, seed) + } else { + seed := make([]byte, 0, md5.Size+sha1.Size) + seed = h.clientMD5.Sum(seed) + seed = h.client.Sum(seed) + prf10(out, masterSecret, clientFinishedLabel, seed) + } + return out } // serverSum returns the contents of the verify_data member of a server's // Finished message. func (h finishedHash) serverSum(masterSecret []byte) []byte { - if h.version == versionSSL30 { - return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic) + if h.version == VersionSSL30 { + return finishedSum30(h.serverMD5, h.server, masterSecret, ssl3ServerFinishedMagic) + } + + out := make([]byte, finishedVerifyLength) + if h.version >= VersionTLS12 { + seed := h.server.Sum(nil) + prf12(out, masterSecret, serverFinishedLabel, seed) + } else { + seed := make([]byte, 0, md5.Size+sha1.Size) + seed = h.serverMD5.Sum(seed) + seed = h.server.Sum(seed) + prf10(out, masterSecret, serverFinishedLabel, seed) + } + return out +} + +// hashForClientCertificate returns a digest, hash function, and TLS 1.2 hash +// id suitable for signing by a TLS client certificate. +func (h finishedHash) hashForClientCertificate(sigType uint8) ([]byte, crypto.Hash, uint8) { + if h.version >= VersionTLS12 { + digest := h.server.Sum(nil) + return digest, crypto.SHA256, hashSHA256 + } + if sigType == signatureECDSA { + digest := h.server.Sum(nil) + return digest, crypto.SHA1, hashSHA1 } - md5 := h.serverMD5.Sum(nil) - sha1 := h.serverSHA1.Sum(nil) - return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret) + digest := make([]byte, 0, 36) + digest = h.serverMD5.Sum(digest) + digest = h.server.Sum(digest) + return digest, crypto.MD5SHA1, 0 /* not specified in TLS 1.2. */ } diff --git a/libgo/go/crypto/tls/prf_test.go b/libgo/go/crypto/tls/prf_test.go index 773a2b2..a9b6c9e 100644 --- a/libgo/go/crypto/tls/prf_test.go +++ b/libgo/go/crypto/tls/prf_test.go @@ -72,7 +72,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) { // These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 ` var testKeysFromTests = []testKeysFromTest{ { - versionTLS10, + VersionTLS10, "0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5", "4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558", "4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db", @@ -85,7 +85,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { - versionTLS10, + VersionTLS10, "03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890", "4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106", "4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c", @@ -98,7 +98,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { - versionTLS10, + VersionTLS10, "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", @@ -111,7 +111,7 @@ var testKeysFromTests = []testKeysFromTest{ 16, }, { - versionSSL30, + VersionSSL30, "832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1", "4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e", "4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e", diff --git a/libgo/go/crypto/tls/tls.go b/libgo/go/crypto/tls/tls.go index 9230656..6c67506 100644 --- a/libgo/go/crypto/tls/tls.go +++ b/libgo/go/crypto/tls/tls.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package tls partially implements TLS 1.0, as specified in RFC 2246. +// Package tls partially implements TLS 1.2, as specified in RFC 5246. package tls import ( diff --git a/libgo/go/crypto/x509/cert_pool.go b/libgo/go/crypto/x509/cert_pool.go index 505f4d4..babe94d 100644 --- a/libgo/go/crypto/x509/cert_pool.go +++ b/libgo/go/crypto/x509/cert_pool.go @@ -25,9 +25,10 @@ func NewCertPool() *CertPool { } // findVerifiedParents attempts to find certificates in s which have signed the -// given certificate. If no such certificate can be found or the signature -// doesn't match, it returns nil. -func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) { +// given certificate. If any candidates were rejected then errCert will be set +// to one of them, arbitrarily, and err will contain the reason that it was +// rejected. +func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) { if s == nil { return } @@ -41,8 +42,10 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) { } for _, c := range candidates { - if cert.CheckSignatureFrom(s.certs[c]) == nil { + if err = cert.CheckSignatureFrom(s.certs[c]); err == nil { parents = append(parents, c) + } else { + errCert = s.certs[c] } } diff --git a/libgo/go/crypto/x509/pkcs1.go b/libgo/go/crypto/x509/pkcs1.go index 873d396..acebe35 100644 --- a/libgo/go/crypto/x509/pkcs1.go +++ b/libgo/go/crypto/x509/pkcs1.go @@ -52,7 +52,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { } if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { - return nil, errors.New("private key contains zero or negative value") + return nil, errors.New("x509: private key contains zero or negative value") } key = new(rsa.PrivateKey) @@ -67,7 +67,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) { key.Primes[1] = priv.Q for i, a := range priv.AdditionalPrimes { if a.Prime.Sign() <= 0 { - return nil, errors.New("private key contains zero or negative prime") + return nil, errors.New("x509: private key contains zero or negative prime") } key.Primes[i+2] = a.Prime // We ignore the other two values because rsa will calculate diff --git a/libgo/go/crypto/x509/pkcs8.go b/libgo/go/crypto/x509/pkcs8.go index 8e1585e..ba19989 100644 --- a/libgo/go/crypto/x509/pkcs8.go +++ b/libgo/go/crypto/x509/pkcs8.go @@ -32,7 +32,7 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { case privKey.Algo.Algorithm.Equal(oidPublicKeyRSA): key, err = ParsePKCS1PrivateKey(privKey.PrivateKey) if err != nil { - return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) + return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) } return key, nil @@ -44,11 +44,11 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { } key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey) if err != nil { - return nil, errors.New("crypto/x509: failed to parse EC private key embedded in PKCS#8: " + err.Error()) + return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error()) } return key, nil default: - return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) + return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) } } diff --git a/libgo/go/crypto/x509/pkix/pkix.go b/libgo/go/crypto/x509/pkix/pkix.go index 7386590..5034946 100644 --- a/libgo/go/crypto/x509/pkix/pkix.go +++ b/libgo/go/crypto/x509/pkix/pkix.go @@ -144,7 +144,7 @@ type CertificateList struct { SignatureValue asn1.BitString } -// HasExpired returns true iff now is past the expiry time of certList. +// HasExpired reports whether now is past the expiry time of certList. func (certList *CertificateList) HasExpired(now time.Time) bool { return now.After(certList.TBSCertList.NextUpdate) } diff --git a/libgo/go/crypto/x509/root_unix.go b/libgo/go/crypto/x509/root_unix.go index 1b25a94..324f855 100644 --- a/libgo/go/crypto/x509/root_unix.go +++ b/libgo/go/crypto/x509/root_unix.go @@ -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 freebsd linux openbsd netbsd +// +build dragonfly freebsd linux openbsd netbsd package x509 @@ -10,11 +10,11 @@ import "io/ioutil" // Possible certificate files; stop after finding one. var certFiles = []string{ - "/etc/ssl/certs/ca-certificates.crt", // Linux etc + "/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc. "/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL "/etc/ssl/ca-bundle.pem", // OpenSUSE "/etc/ssl/cert.pem", // OpenBSD - "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD + "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly } func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { diff --git a/libgo/go/crypto/x509/root_windows.go b/libgo/go/crypto/x509/root_windows.go index e8f70a4..81018b7 100644 --- a/libgo/go/crypto/x509/root_windows.go +++ b/libgo/go/crypto/x509/root_windows.go @@ -89,7 +89,7 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e case syscall.CERT_TRUST_IS_NOT_TIME_VALID: return CertificateInvalidError{c, Expired} default: - return UnknownAuthorityError{c} + return UnknownAuthorityError{c, nil, nil} } } return nil @@ -129,9 +129,9 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex case syscall.CERT_E_CN_NO_MATCH: return HostnameError{c, opts.DNSName} case syscall.CERT_E_UNTRUSTEDROOT: - return UnknownAuthorityError{c} + return UnknownAuthorityError{c, nil, nil} default: - return UnknownAuthorityError{c} + return UnknownAuthorityError{c, nil, nil} } } diff --git a/libgo/go/crypto/x509/sec1.go b/libgo/go/crypto/x509/sec1.go index 8a2840f..7de6675 100644 --- a/libgo/go/crypto/x509/sec1.go +++ b/libgo/go/crypto/x509/sec1.go @@ -33,6 +33,20 @@ func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) { return parseECPrivateKey(nil, der) } +// MarshalECPrivateKey marshals an EC private key into ASN.1, DER format. +func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) { + oid, ok := oidFromNamedCurve(key.Curve) + if !ok { + return nil, errors.New("x509: unknown elliptic curve") + } + return asn1.Marshal(ecPrivateKey{ + Version: 1, + PrivateKey: key.D.Bytes(), + NamedCurveOID: oid, + PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)}, + }) +} + // parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure. // The OID for the named curve may be provided from another source (such as // the PKCS8 container) - if it is provided then use this instead of the OID @@ -40,10 +54,10 @@ func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) { func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) { var privKey ecPrivateKey if _, err := asn1.Unmarshal(der, &privKey); err != nil { - return nil, errors.New("crypto/x509: failed to parse EC private key: " + err.Error()) + return nil, errors.New("x509: failed to parse EC private key: " + err.Error()) } if privKey.Version != ecPrivKeyVersion { - return nil, fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version) + return nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version) } var curve elliptic.Curve @@ -53,12 +67,12 @@ func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *e curve = namedCurveFromOID(privKey.NamedCurveOID) } if curve == nil { - return nil, errors.New("crypto/x509: unknown elliptic curve") + return nil, errors.New("x509: unknown elliptic curve") } k := new(big.Int).SetBytes(privKey.PrivateKey) if k.Cmp(curve.Params().N) >= 0 { - return nil, errors.New("crypto/x509: invalid elliptic curve private key value") + return nil, errors.New("x509: invalid elliptic curve private key value") } priv := new(ecdsa.PrivateKey) priv.Curve = curve diff --git a/libgo/go/crypto/x509/sec1_test.go b/libgo/go/crypto/x509/sec1_test.go index 7135699..95f18e7 100644 --- a/libgo/go/crypto/x509/sec1_test.go +++ b/libgo/go/crypto/x509/sec1_test.go @@ -5,6 +5,7 @@ package x509 import ( + "bytes" "encoding/hex" "testing" ) @@ -15,8 +16,15 @@ var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4d func TestParseECPrivateKey(t *testing.T) { derBytes, _ := hex.DecodeString(ecPrivateKeyHex) - _, err := ParseECPrivateKey(derBytes) + key, err := ParseECPrivateKey(derBytes) if err != nil { t.Errorf("failed to decode EC private key: %s", err) } + serialized, err := MarshalECPrivateKey(key) + if err != nil { + t.Fatalf("failed to encode EC private key: %s", err) + } + if !bytes.Equal(serialized, derBytes) { + t.Fatalf("serialized key differs: got %x, want %x", serialized, derBytes) + } } diff --git a/libgo/go/crypto/x509/verify.go b/libgo/go/crypto/x509/verify.go index b29ddbc..8327463 100644 --- a/libgo/go/crypto/x509/verify.go +++ b/libgo/go/crypto/x509/verify.go @@ -5,6 +5,7 @@ package x509 import ( + "fmt" "net" "runtime" "strings" @@ -91,10 +92,27 @@ func (h HostnameError) Error() string { // UnknownAuthorityError results when the certificate issuer is unknown type UnknownAuthorityError struct { cert *Certificate + // hintErr contains an error that may be helpful in determining why an + // authority wasn't found. + hintErr error + // hintCert contains a possible authority certificate that was rejected + // because of the error in hintErr. + hintCert *Certificate } func (e UnknownAuthorityError) Error() string { - return "x509: certificate signed by unknown authority" + s := "x509: certificate signed by unknown authority" + if e.hintErr != nil { + certName := e.hintCert.Subject.CommonName + if len(certName) == 0 { + if len(e.hintCert.Subject.Organization) > 0 { + certName = e.hintCert.Subject.Organization[0] + } + certName = "serial:" + e.hintCert.SerialNumber.String() + } + s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName) + } + return s } // SystemRootsError results when we fail to load the system root certificates. @@ -136,14 +154,18 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V } if len(c.PermittedDNSDomains) > 0 { + ok := false for _, domain := range c.PermittedDNSDomains { if opts.DNSName == domain || (strings.HasSuffix(opts.DNSName, domain) && len(opts.DNSName) >= 1+len(domain) && opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') { - continue + ok = true + break } + } + if !ok { return CertificateInvalidError{c, CANotAuthorizedForThisName} } } @@ -249,7 +271,8 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate } func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) { - for _, rootNum := range opts.Roots.findVerifiedParents(c) { + possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c) + for _, rootNum := range possibleRoots { root := opts.Roots.certs[rootNum] err = root.isValid(rootCertificate, currentChain, opts) if err != nil { @@ -258,8 +281,9 @@ func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain [ chains = append(chains, appendToFreshChain(currentChain, root)) } + possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c) nextIntermediate: - for _, intermediateNum := range opts.Intermediates.findVerifiedParents(c) { + for _, intermediateNum := range possibleIntermediates { intermediate := opts.Intermediates.certs[intermediateNum] for _, cert := range currentChain { if cert == intermediate { @@ -284,7 +308,13 @@ nextIntermediate: } if len(chains) == 0 && err == nil { - err = UnknownAuthorityError{c} + hintErr := rootErr + hintCert := failedRoot + if hintErr == nil { + hintErr = intermediateErr + hintCert = failedIntermediate + } + err = UnknownAuthorityError{c, hintErr, hintCert} } return diff --git a/libgo/go/crypto/x509/verify_test.go b/libgo/go/crypto/x509/verify_test.go index 5103ed8..ba6c13d 100644 --- a/libgo/go/crypto/x509/verify_test.go +++ b/libgo/go/crypto/x509/verify_test.go @@ -127,6 +127,18 @@ var verifyTests = []verifyTest{ }, }, { + leaf: googleLeafWithInvalidHash, + intermediates: []string{thawteIntermediate}, + roots: []string{verisignRoot}, + currentTime: 1302726541, + dnsName: "www.google.com", + + // The specific error message may not occur when using system + // verification. + systemSkip: true, + errorCallback: expectHashError, + }, + { // The default configuration should reject an S/MIME chain. leaf: smimeLeaf, roots: []string{smimeIntermediate}, @@ -171,6 +183,24 @@ var verifyTests = []verifyTest{ {"mega.co.nz", "EssentialSSL CA", "COMODO Certification Authority"}, }, }, + { + // Check that a name constrained intermediate works even when + // it lists multiple constraints. + leaf: nameConstraintsLeaf, + intermediates: []string{nameConstraintsIntermediate1, nameConstraintsIntermediate2}, + roots: []string{globalSignRoot}, + currentTime: 1382387896, + dnsName: "secure.iddl.vt.edu", + + expectedChains: [][]string{ + { + "Technology-enhanced Learning and Online Strategies", + "Virginia Tech Global Qualified Server CA", + "Trusted Root CA G2", + "GlobalSign Root CA", + }, + }, + }, } func expectHostnameError(t *testing.T, i int, err error) (ok bool) { @@ -213,6 +243,18 @@ func expectSystemRootsError(t *testing.T, i int, err error) bool { return true } +func expectHashError(t *testing.T, i int, err error) bool { + if err == nil { + t.Errorf("#%d: no error resulted from invalid hash", i) + return false + } + if expected := "algorithm unimplemented"; !strings.Contains(err.Error(), expected) { + t.Errorf("#%d: error resulting from invalid hash didn't contain '%s', rather it was: %s", i, expected, err) + return false + } + return true +} + func certificateFromPEM(pemBytes string) (*Certificate, error) { block, _ := pem.Decode([]byte(pemBytes)) if block == nil { @@ -400,6 +442,28 @@ u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6 z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw== -----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-----` + const dnssecExpLeaf = `-----BEGIN CERTIFICATE----- MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0 @@ -522,6 +586,50 @@ um0ABj6y6koQOdjQK/W/7HW/lwLFCRsI3FU34oH7N4RDYiDK51ZLZer+bMEkkySh NOsF/5oirpt9P/FlUQqmMGqz9IgcgA38corog14= -----END CERTIFICATE-----` +const startComRootSHA256 = `-----BEGIN CERTIFICATE----- +MIIHhzCCBW+gAwIBAgIBLTANBgkqhkiG9w0BAQsFADB9MQswCQYDVQQGEwJJTDEW +MBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0YWwg +Q2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3RhcnRDb20gQ2VydGlmaWNh +dGlvbiBBdXRob3JpdHkwHhcNMDYwOTE3MTk0NjM3WhcNMzYwOTE3MTk0NjM2WjB9 +MQswCQYDVQQGEwJJTDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMi +U2VjdXJlIERpZ2l0YWwgQ2VydGlmaWNhdGUgU2lnbmluZzEpMCcGA1UEAxMgU3Rh +cnRDb20gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUA +A4ICDwAwggIKAoICAQDBiNsJvGxGfHiflXu1M5DycmLWwTYgIiRezul38kMKogZk +pMyONvg45iPwbm2xPN1yo4UcodM9tDMr0y+v/uqwQVlntsQGfQqedIXWeUyAN3rf +OQVSWff0G0ZDpNKFhdLDcfN1YjS6LIp/Ho/u7TTQEceWzVI9ujPW3U3eCztKS5/C +Ji/6tRYccjV3yjxd5srhJosaNnZcAdt0FCX+7bWgiA/deMotHweXMAEtcnn6RtYT +Kqi5pquDSR3l8u/d5AGOGAqPY1MWhWKpDhk6zLVmpsJrdAfkK+F2PrRt2PZE4XNi +HzvEvqBTViVsUQn3qqvKv3b9bZvzndu/PWa8DFaqr5hIlTpL36dYUNk4dalb6kMM +Av+Z6+hsTXBbKWWc3apdzK8BMewM69KN6Oqce+Zu9ydmDBpI125C4z/eIT574Q1w ++2OqqGwaVLRcJXrJosmLFqa7LH4XXgVNWG4SHQHuEhANxjJ/GP/89PrNbpHoNkm+ +Gkhpi8KWTRoSsmkXwQqQ1vp5Iki/untp+HDH+no32NgN0nZPV/+Qt+OR0t3vwmC3 +Zzrd/qqc8NSLf3Iizsafl7b4r4qgEKjZ+xjGtrVcUjyJthkqcwEKDwOzEmDyei+B +26Nu/yYwl/WL3YlXtq09s68rxbd2AvCl1iuahhQqcvbjM4xdCUsT37uMdBNSSwID +AQABo4ICEDCCAgwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFE4L7xqkQFulF2mHMMo0aEPQQa7yMB8GA1UdIwQYMBaAFE4L7xqkQFul +F2mHMMo0aEPQQa7yMIIBWgYDVR0gBIIBUTCCAU0wggFJBgsrBgEEAYG1NwEBATCC +ATgwLgYIKwYBBQUHAgEWImh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL3BvbGljeS5w +ZGYwNAYIKwYBBQUHAgEWKGh0dHA6Ly93d3cuc3RhcnRzc2wuY29tL2ludGVybWVk +aWF0ZS5wZGYwgc8GCCsGAQUFBwICMIHCMCcWIFN0YXJ0IENvbW1lcmNpYWwgKFN0 +YXJ0Q29tKSBMdGQuMAMCAQEagZZMaW1pdGVkIExpYWJpbGl0eSwgcmVhZCB0aGUg +c2VjdGlvbiAqTGVnYWwgTGltaXRhdGlvbnMqIG9mIHRoZSBTdGFydENvbSBDZXJ0 +aWZpY2F0aW9uIEF1dGhvcml0eSBQb2xpY3kgYXZhaWxhYmxlIGF0IGh0dHA6Ly93 +d3cuc3RhcnRzc2wuY29tL3BvbGljeS5wZGYwEQYJYIZIAYb4QgEBBAQDAgAHMDgG +CWCGSAGG+EIBDQQrFilTdGFydENvbSBGcmVlIFNTTCBDZXJ0aWZpY2F0aW9uIEF1 +dGhvcml0eTANBgkqhkiG9w0BAQsFAAOCAgEAjo/n3JR5fPGFf59Jb2vKXfuM/gTF +wWLRfUKKvFO3lANmMD+x5wqnUCBVJX92ehQN6wQOQOY+2IirByeDqXWmN3PH/UvS +Ta0XQMhGvjt/UfzDtgUx3M2FIk5xt/JxXrAaxrqTi3iSSoX4eA+D/i+tLPfkpLst +0OcNOrg+zvZ49q5HJMqjNTbOx8aHmNrs++myziebiMMEofYLWWivydsQD032ZGNc +pRJvkrKTlMeIFw6Ttn5ii5B/q06f/ON1FE8qMt9bDeD1e5MNq6HPh+GlBEXoPBKl +CcWw0bdT82AUuoVpaiF8H3VhFyAXe2w7QSlc4axa0c2Mm+tgHRns9+Ww2vl5GKVF +P0lDV9LdJNUso/2RjSe15esUBppMeyG7Oq0wBhjA2MFrLH9ZXF2RsXAiV+uKa0hK +1Q8p7MZAwC+ITGgBF3f0JBlPvfrhsiAhS90a2Cl9qrjeVOwhVYBsHvUwyKMQ5bLm +KhQxw4UtjJixhlpPiVktucf3HMiKf8CdBUrmQk9io20ppB+Fq9vlgcitKj1MXVuE +JnHEhV5xJMqlG2zYYdMa4FTbzrqpMrUi9nNBCV24F10OD5mQ1kfabwo6YigUZ4LZ +8dCAWZvLMdibD4x3TrVoivJs9iQOLWxwxXPR3hTQcY+203sC9uO41Alua551hDnm +fyWl8kgAwKQB2j8= +-----END CERTIFICATE-----` + const smimeLeaf = `-----BEGIN CERTIFICATE----- MIIFBjCCA+6gAwIBAgISESFvrjT8XcJTEe6rBlPptILlMA0GCSqGSIb3DQEBBQUA MFQxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMSowKAYD @@ -663,3 +771,168 @@ zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB ZQ== -----END CERTIFICATE-----` + +var nameConstraintsLeaf = `-----BEGIN CERTIFICATE----- +MIIHMTCCBRmgAwIBAgIIIZaV/3ezOJkwDQYJKoZIhvcNAQEFBQAwgcsxCzAJBgNV +BAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEj +MCEGA1UECxMaR2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1Zp +cmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0 +eTExMC8GA1UEAxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZl +ciBDQTAeFw0xMzA5MTkxNDM2NTVaFw0xNTA5MTkxNDM2NTVaMIHNMQswCQYDVQQG +EwJVUzERMA8GA1UECAwIVmlyZ2luaWExEzARBgNVBAcMCkJsYWNrc2J1cmcxPDA6 +BgNVBAoMM1ZpcmdpbmlhIFBvbHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUg +VW5pdmVyc2l0eTE7MDkGA1UECwwyVGVjaG5vbG9neS1lbmhhbmNlZCBMZWFybmlu +ZyBhbmQgT25saW5lIFN0cmF0ZWdpZXMxGzAZBgNVBAMMEnNlY3VyZS5pZGRsLnZ0 +LmVkdTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkOyPpsOK/6IuPG +WnIBlVwlHzeYf+cUlggqkLq0b0+vZbiTXgio9/VCuNQ8opSoss7J7o3ygV9to+9Y +YwJKVC5WDT/y5JWpQey0CWILymViJnpNSwnxBc8A+Q8w5NUGDd/UhtPx/U8/hqbd +WPDYj2hbOqyq8UlRhfS5pwtnv6BbCTaY11I6FhCLK7zttISyTuWCf9p9o/ggiipP +ii/5oh4dkl+r5SfuSp5GPNHlYO8lWqys5NAPoDD4fc/kuflcK7Exx7XJ+Oqu0W0/ +psjEY/tES1ZgDWU/ParcxxFpFmKHbD5DXsfPOObzkVWXIY6tGMutSlE1Froy/Nn0 +OZsAOrcCAwEAAaOCAhMwggIPMIG4BggrBgEFBQcBAQSBqzCBqDBYBggrBgEFBQcw +AoZMaHR0cDovL3d3dy5wa2kudnQuZWR1L2dsb2JhbHF1YWxpZmllZHNlcnZlci9j +YWNlcnQvZ2xvYmFscXVhbGlmaWVkc2VydmVyLmNydDBMBggrBgEFBQcwAYZAaHR0 +cDovL3Z0Y2EtcC5lcHJvdi5zZXRpLnZ0LmVkdTo4MDgwL2VqYmNhL3B1YmxpY3dl +Yi9zdGF0dXMvb2NzcDAdBgNVHQ4EFgQUp7xbO6iHkvtZbPE4jmndmnAbSEcwDAYD +VR0TAQH/BAIwADAfBgNVHSMEGDAWgBS8YmAn1eM1SBfpS6tFatDIqHdxjDBqBgNV +HSAEYzBhMA4GDCsGAQQBtGgFAgICATAOBgwrBgEEAbRoBQICAQEwPwYMKwYBBAG0 +aAUCAgMBMC8wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucGtpLnZ0LmVkdS9nbG9i +YWwvY3BzLzBKBgNVHR8EQzBBMD+gPaA7hjlodHRwOi8vd3d3LnBraS52dC5lZHUv +Z2xvYmFscXVhbGlmaWVkc2VydmVyL2NybC9jYWNybC5jcmwwDgYDVR0PAQH/BAQD +AgTwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHREEFjAUghJz +ZWN1cmUuaWRkbC52dC5lZHUwDQYJKoZIhvcNAQEFBQADggIBAEgoYo4aUtatY3gI +OyyKp7QlIOaLbTJZywESHqy+L5EGDdJW2DJV+mcE0LDGvqa2/1Lo+AR1ntsZwfOi +Y718JwgVVaX/RCd5+QKP25c5/x72xI8hb/L1bgS0ED9b0YAhd7Qm1K1ot82+6mqX +DW6WiGeDr8Z07MQ3143qQe2rBlq+QI69DYzm2GOqAIAnUIWv7tCyLUm31b4DwmrJ +TeudVreTKUbBNB1TWRFHEPkWhjjXKZnNGRO11wHXcyBu6YekIvVZ+vmx8ePee4jJ +3GFOi7lMuWOeq57jTVL7KOKaKLVXBb6gqo5aq+Wwt8RUD5MakrCAEeQZj7DKaFmZ +oQCO0Pxrsl3InCGvxnGzT+bFVO9nJ/BAMj7hknFdm9Jr6Bg5q33Z+gnf909AD9QF +ESqUSykaHu2LVdJx2MaCH1CyKnRgMw5tEwE15EXpUjCm24m8FMOYC+rNtf18pgrz +5D8Jhh+oxK9PjcBYqXNtnioIxiMCYcV0q5d4w4BYFEh71tk7/bYB0R55CsBUVPmp +timWNOdRd57Tfpk3USaVsumWZAf9MP3wPiC7gb4d5tYEEAG5BuDT8ruFw838wU8G +1VvAVutSiYBg7k3NYO7AUqZ+Ax4klQX3aM9lgonmJ78Qt94UPtbptrfZ4/lSqEf8 +GBUwDrQNTb+gsXsDkjd5lcYxNx6l +-----END CERTIFICATE-----` + +var nameConstraintsIntermediate1 = `-----BEGIN CERTIFICATE----- +MIINLjCCDBagAwIBAgIRIqpyf/YoGgvHc8HiDAxAI8owDQYJKoZIhvcNAQEFBQAw +XDELMAkGA1UEBhMCQkUxFTATBgNVBAsTDFRydXN0ZWQgUm9vdDEZMBcGA1UEChMQ +R2xvYmFsU2lnbiBudi1zYTEbMBkGA1UEAxMSVHJ1c3RlZCBSb290IENBIEcyMB4X +DTEyMTIxMzAwMDAwMFoXDTE3MTIxMzAwMDAwMFowgcsxCzAJBgNVBAYTAlVTMREw +DwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEjMCEGA1UECxMa +R2xvYmFsIFF1YWxpZmllZCBTZXJ2ZXIgQ0ExPDA6BgNVBAoTM1ZpcmdpbmlhIFBv +bHl0ZWNobmljIEluc3RpdHV0ZSBhbmQgU3RhdGUgVW5pdmVyc2l0eTExMC8GA1UE +AxMoVmlyZ2luaWEgVGVjaCBHbG9iYWwgUXVhbGlmaWVkIFNlcnZlciBDQTCCAiIw +DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALgIZhEaptBWADBqdJ45ueFGzMXa +GHnzNxoxR1fQIaaRQNdCg4cw3A4dWKMeEgYLtsp65ai3Xfw62Qaus0+KJ3RhgV+r +ihqK81NUzkls78fJlADVDI4fCTlothsrE1CTOMiy97jKHai5mVTiWxmcxpmjv7fm +5Nhc+uHgh2hIz6npryq495mD51ZrUTIaqAQN6Pw/VHfAmR524vgriTOjtp1t4lA9 +pXGWjF/vkhAKFFheOQSQ00rngo2wHgCqMla64UTN0oz70AsCYNZ3jDLx0kOP0YmM +R3Ih91VA63kLqPXA0R6yxmmhhxLZ5bcyAy1SLjr1N302MIxLM/pSy6aquEnbELhz +qyp9yGgRyGJay96QH7c4RJY6gtcoPDbldDcHI9nXngdAL4DrZkJ9OkDkJLyqG66W +ZTF5q4EIs6yMdrywz0x7QP+OXPJrjYpbeFs6tGZCFnWPFfmHCRJF8/unofYrheq+ +9J7Jx3U55S/k57NXbAM1RAJOuMTlfn9Etf9Dpoac9poI4Liav6rBoUQk3N3JWqnV +HNx/NdCyJ1/6UbKMJUZsStAVglsi6lVPo289HHOE4f7iwl3SyekizVOp01wUin3y +cnbZB/rXmZbwapSxTTSBf0EIOr9i4EGfnnhCAVA9U5uLrI5OEB69IY8PNX0071s3 +Z2a2fio5c8m3JkdrAgMBAAGjggh5MIIIdTAOBgNVHQ8BAf8EBAMCAQYwTAYDVR0g +BEUwQzBBBgkrBgEEAaAyATwwNDAyBggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xv +YmFsc2lnbi5jb20vcmVwb3NpdG9yeS8wEgYDVR0TAQH/BAgwBgEB/wIBADCCBtAG +A1UdHgSCBscwggbDoIIGvzASghAzZGJsYWNrc2J1cmcub3JnMBiCFmFjY2VsZXJh +dGV2aXJnaW5pYS5jb20wGIIWYWNjZWxlcmF0ZXZpcmdpbmlhLm9yZzALgglhY3Zj +cC5vcmcwCYIHYmV2Lm5ldDAJggdiZXYub3JnMAuCCWNsaWdzLm9yZzAMggpjbWl3 +ZWIub3JnMBeCFWVhc3Rlcm5icm9va3Ryb3V0Lm5ldDAXghVlYXN0ZXJuYnJvb2t0 +cm91dC5vcmcwEYIPZWNvcnJpZG9ycy5pbmZvMBOCEWVkZ2FycmVzZWFyY2gub3Jn +MBKCEGdldC1lZHVjYXRlZC5jb20wE4IRZ2V0LWVkdWNhdGVkLmluZm8wEYIPZ2V0 +ZWR1Y2F0ZWQubmV0MBKCEGdldC1lZHVjYXRlZC5uZXQwEYIPZ2V0ZWR1Y2F0ZWQu +b3JnMBKCEGdldC1lZHVjYXRlZC5vcmcwD4INaG9raWVjbHViLmNvbTAQgg5ob2tp +ZXBob3RvLmNvbTAPgg1ob2tpZXNob3AuY29tMBGCD2hva2llc3BvcnRzLmNvbTAS +ghBob2tpZXRpY2tldHMuY29tMBKCEGhvdGVscm9hbm9rZS5jb20wE4IRaHVtYW53 +aWxkbGlmZS5vcmcwF4IVaW5uYXR2aXJnaW5pYXRlY2guY29tMA+CDWlzY2hwMjAx +MS5vcmcwD4INbGFuZHJlaGFiLm9yZzAggh5uYXRpb25hbHRpcmVyZXNlYXJjaGNl +bnRlci5jb20wFYITbmV0d29ya3ZpcmdpbmlhLm5ldDAMggpwZHJjdnQuY29tMBiC +FnBldGVkeWVyaXZlcmNvdXJzZS5jb20wDYILcmFkaW9pcS5vcmcwFYITcml2ZXJj +b3Vyc2Vnb2xmLmNvbTALgglzZGltaS5vcmcwEIIOc292YW1vdGlvbi5jb20wHoIc +c3VzdGFpbmFibGUtYmlvbWF0ZXJpYWxzLmNvbTAeghxzdXN0YWluYWJsZS1iaW9t +YXRlcmlhbHMub3JnMBWCE3RoaXNpc3RoZWZ1dHVyZS5jb20wGIIWdGhpcy1pcy10 +aGUtZnV0dXJlLmNvbTAVghN0aGlzaXN0aGVmdXR1cmUubmV0MBiCFnRoaXMtaXMt +dGhlLWZ1dHVyZS5uZXQwCoIIdmFkcy5vcmcwDIIKdmFsZWFmLm9yZzANggt2YXRl +Y2guaW5mbzANggt2YXRlY2gubW9iaTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5n +LmNvbTAcghp2YXRlY2hsaWZlbG9uZ2xlYXJuaW5nLm5ldDAcghp2YXRlY2hsaWZl +bG9uZ2xlYXJuaW5nLm9yZzAKggh2Y29tLmVkdTASghB2aXJnaW5pYXZpZXcubmV0 +MDSCMnZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVuaXZlcnNp +dHkuY29tMDWCM3ZpcmdpbmlhcG9seXRlY2huaWNpbnN0aXR1dGVhbmRzdGF0ZXVu +aXZlcnNpdHkuaW5mbzA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0dXRlYW5k +c3RhdGV1bml2ZXJzaXR5Lm5ldDA0gjJ2aXJnaW5pYXBvbHl0ZWNobmljaW5zdGl0 +dXRlYW5kc3RhdGV1bml2ZXJzaXR5Lm9yZzAZghd2aXJnaW5pYXB1YmxpY3JhZGlv +Lm9yZzASghB2aXJnaW5pYXRlY2guZWR1MBOCEXZpcmdpbmlhdGVjaC5tb2JpMByC +GnZpcmdpbmlhdGVjaGZvdW5kYXRpb24ub3JnMAiCBnZ0LmVkdTALggl2dGFyYy5v +cmcwDIIKdnQtYXJjLm9yZzALggl2dGNyYy5jb20wCoIIdnRpcC5vcmcwDIIKdnRs +ZWFuLm9yZzAWghR2dGtub3dsZWRnZXdvcmtzLmNvbTAYghZ2dGxpZmVsb25nbGVh +cm5pbmcuY29tMBiCFnZ0bGlmZWxvbmdsZWFybmluZy5uZXQwGIIWdnRsaWZlbG9u +Z2xlYXJuaW5nLm9yZzATghF2dHNwb3J0c21lZGlhLmNvbTALggl2dHdlaS5jb20w +D4INd2l3YXR3ZXJjLmNvbTAKggh3dnRmLm9yZzAIgQZ2dC5lZHUwd6R1MHMxCzAJ +BgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTETMBEGA1UEBxMKQmxhY2tzYnVy +ZzE8MDoGA1UEChMzVmlyZ2luaWEgUG9seXRlY2huaWMgSW5zdGl0dXRlIGFuZCBT +dGF0ZSBVbml2ZXJzaXR5MCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDAQYI +KwYBBQUHAwkwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2NybC5nbG9iYWxzaWdu +LmNvbS9ncy90cnVzdHJvb3RnMi5jcmwwgYQGCCsGAQUFBwEBBHgwdjAzBggrBgEF +BQcwAYYnaHR0cDovL29jc3AyLmdsb2JhbHNpZ24uY29tL3RydXN0cm9vdGcyMD8G +CCsGAQUFBzAChjNodHRwOi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC90 +cnVzdHJvb3RnMi5jcnQwHQYDVR0OBBYEFLxiYCfV4zVIF+lLq0Vq0Miod3GMMB8G +A1UdIwQYMBaAFBT25YsxtkWASkxt/MKHico2w5BiMA0GCSqGSIb3DQEBBQUAA4IB +AQAyJm/lOB2Er4tHXhc/+fSufSzgjohJgYfMkvG4LknkvnZ1BjliefR8tTXX49d2 +SCDFWfGjqyJZwavavkl/4p3oXPG/nAMDMvxh4YAT+CfEK9HH+6ICV087kD4BLegi ++aFJMj8MMdReWCzn5sLnSR1rdse2mo2arX3Uod14SW+PGrbUmTuWNyvRbz3fVmxp +UdbGmj3laknO9YPsBGgHfv73pVVsTJkW4ZfY/7KdD/yaVv6ophpOB3coXfjl2+kd +Z4ypn2zK+cx9IL/LSewqd/7W9cD55PCUy4X9OTbEmAccwiz3LB66mQoUGfdHdkoB +jUY+v9vLQXmaVwI0AYL7g9LN +-----END CERTIFICATE-----` + +var nameConstraintsIntermediate2 = `-----BEGIN CERTIFICATE----- +MIIEXTCCA0WgAwIBAgILBAAAAAABNuk6OrMwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw0xMjA0MjUxMTAw +MDBaFw0yNzA0MjUxMTAwMDBaMFwxCzAJBgNVBAYTAkJFMRUwEwYDVQQLEwxUcnVz +dGVkIFJvb3QxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExGzAZBgNVBAMTElRy +dXN0ZWQgUm9vdCBDQSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB +AKyuvqrtcMr7g7EuNbu4sKwxM127UsCmx1RxbxxgcArGS7rjiefpBH/w4LYrymjf +vcw1ueyMNoqLo9nJMz/ORXupb35NNfE667prQYHa+tTjl1IiKpB7QUwt3wXPuTMF +Ja1tXtjKzkqJyuJlNuPKT76HcjgNqgV1s9qG44MD5I2JvI12du8zI1bgdQ+l/KsX +kTfbGjUvhOLOlVNWVQDpL+YMIrGqgBYxy5TUNgrAcRtwpNdS2KkF5otSmMweVb5k +hoUVv3u8UxQH/WWbNhHq1RrIlg/0rBUfi/ziShYFSB7U+aLx5DxPphTFBiDquQGp +tB+FC4JvnukDStFihZCZ1R8CAwEAAaOCASMwggEfMA4GA1UdDwEB/wQEAwIBBjAP +BgNVHRMBAf8EBTADAQH/MEcGA1UdIARAMD4wPAYEVR0gADA0MDIGCCsGAQUFBwIB +FiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAdBgNVHQ4E +FgQUFPblizG2RYBKTG38woeJyjbDkGIwMwYDVR0fBCwwKjAooCagJIYiaHR0cDov +L2NybC5nbG9iYWxzaWduLm5ldC9yb290LmNybDA+BggrBgEFBQcBAQQyMDAwLgYI +KwYBBQUHMAGGImh0dHA6Ly9vY3NwMi5nbG9iYWxzaWduLmNvbS9yb290cjEwHwYD +VR0jBBgwFoAUYHtmGkUNl8qJUC99BM00qP/8/UswDQYJKoZIhvcNAQEFBQADggEB +AL7IG0l+k4LkcpI+a/kvZsSRwSM4uA6zGX34e78A2oytr8RG8bJwVb8+AHMUD+Xe +2kYdh/Uj/waQXfqR0OgxQXL9Ct4ZM+JlR1avsNKXWL5AwYXAXCOB3J5PW2XOck7H +Zw0vRbGQhjWjQx+B4KOUFg1b3ov/z6Xkr3yaCfRQhXh7KC0Bc0RXPPG5Nv5lCW+z +tbbg0zMm3kyfQITRusMSg6IBsDJqOnjaiaKQRcXiD0Sk43ZXb2bUKMxC7+Td3QL4 +RyHcWJbQ7YylLTS/x+jxWIcOQ0oO5/54t5PTQ14neYhOz9x4gUk2AYAW6d1vePwb +hcC8roQwkHT7HvfYBoc74FM= +-----END CERTIFICATE-----` + +var globalSignRoot = `-----BEGIN CERTIFICATE----- +MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG +A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv +b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw +MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i +YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT +aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ +jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp +xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp +1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG +snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ +U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 +9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E +BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B +AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz +yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE +38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP +AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad +DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME +HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== +-----END CERTIFICATE-----` diff --git a/libgo/go/crypto/x509/x509.go b/libgo/go/crypto/x509/x509.go index 4dfea2c..57f68ba 100644 --- a/libgo/go/crypto/x509/x509.go +++ b/libgo/go/crypto/x509/x509.go @@ -40,38 +40,60 @@ func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error) { } algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm) if algo == UnknownPublicKeyAlgorithm { - return nil, errors.New("ParsePKIXPublicKey: unknown public key algorithm") + return nil, errors.New("x509: unknown public key algorithm") } return parsePublicKey(algo, &pki) } -// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. -func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { - var pubBytes []byte - +func marshalPublicKey(pub interface{}) (publicKeyBytes []byte, publicKeyAlgorithm pkix.AlgorithmIdentifier, err error) { switch pub := pub.(type) { case *rsa.PublicKey: - pubBytes, _ = asn1.Marshal(rsaPublicKey{ + publicKeyBytes, err = asn1.Marshal(rsaPublicKey{ N: pub.N, E: pub.E, }) + publicKeyAlgorithm.Algorithm = oidPublicKeyRSA + // This is a NULL parameters value which is technically + // superfluous, but most other code includes it and, by + // doing this, we match their public key hashes. + publicKeyAlgorithm.Parameters = asn1.RawValue{ + Tag: 5, + } + case *ecdsa.PublicKey: + publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) + oid, ok := oidFromNamedCurve(pub.Curve) + if !ok { + return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: unsupported elliptic curve") + } + publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA + var paramBytes []byte + paramBytes, err = asn1.Marshal(oid) + if err != nil { + return + } + publicKeyAlgorithm.Parameters.FullBytes = paramBytes default: - return nil, errors.New("MarshalPKIXPublicKey: unknown public key type") + return nil, pkix.AlgorithmIdentifier{}, errors.New("x509: only RSA and ECDSA public keys supported") + } + + return publicKeyBytes, publicKeyAlgorithm, nil +} + +// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format. +func MarshalPKIXPublicKey(pub interface{}) ([]byte, error) { + var publicKeyBytes []byte + var publicKeyAlgorithm pkix.AlgorithmIdentifier + var err error + + if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { + return nil, err } pkix := pkixPublicKey{ - Algo: pkix.AlgorithmIdentifier{ - Algorithm: []int{1, 2, 840, 113549, 1, 1, 1}, - // This is a NULL parameters value which is technically - // superfluous, but most other code includes it and, by - // doing this, we match their public key hashes. - Parameters: asn1.RawValue{ - Tag: 5, - }, - }, + Algo: publicKeyAlgorithm, BitString: asn1.BitString{ - Bytes: pubBytes, - BitLength: 8 * len(pubBytes), + Bytes: publicKeyBytes, + BitLength: 8 * len(publicKeyBytes), }, } @@ -453,6 +475,18 @@ type Certificate struct { NotBefore, NotAfter time.Time // Validity bounds. KeyUsage KeyUsage + // Extensions contains raw X.509 extensions. When parsing certificates, + // this can be used to extract non-critical extensions that are not + // parsed by this package. When marshaling certificates, the Extensions + // field is ignored, see ExtraExtensions. + Extensions []pkix.Extension + + // ExtraExtensions contains extensions to be copied, raw, into any + // marshaled certificates. Values override any extensions that would + // otherwise be produced based on the other fields. The ExtraExtensions + // field is not populated when parsing certificates, see Extensions. + ExtraExtensions []pkix.Extension + ExtKeyUsage []ExtKeyUsage // Sequence of extended key usages. UnknownExtKeyUsage []asn1.ObjectIdentifier // Encountered extended key usages unknown to this package. @@ -463,6 +497,10 @@ type Certificate struct { SubjectKeyId []byte AuthorityKeyId []byte + // RFC 5280, 4.2.2.1 (Authority Information Access) + OCSPServer []string + IssuingCertificateURL []string + // Subject Alternate Name values DNSNames []string EmailAddresses []string @@ -472,12 +510,15 @@ type Certificate struct { PermittedDNSDomainsCritical bool // if true then the name constraints are marked critical. PermittedDNSDomains []string + // CRL Distribution Points + CRLDistributionPoints []string + PolicyIdentifiers []asn1.ObjectIdentifier } // ErrUnsupportedAlgorithm results from attempting to perform an operation that // involves algorithms that are not currently implemented. -var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature: algorithm unimplemented") +var ErrUnsupportedAlgorithm = errors.New("x509: cannot verify signature: algorithm unimplemented") // ConstraintViolationError results when a requested usage is not permitted by // a certificate. For example: checking a signature when the public key isn't a @@ -485,7 +526,7 @@ var ErrUnsupportedAlgorithm = errors.New("crypto/x509: cannot verify signature: type ConstraintViolationError struct{} func (ConstraintViolationError) Error() string { - return "crypto/x509: invalid signature: parent certificate cannot sign this kind of certificate" + return "x509: invalid signature: parent certificate cannot sign this kind of certificate" } func (c *Certificate) Equal(other *Certificate) bool { @@ -604,10 +645,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return err } if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { - return errors.New("DSA signature contained zero or negative values") + return errors.New("x509: DSA signature contained zero or negative values") } if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { - return errors.New("DSA verification failure") + return errors.New("x509: DSA verification failure") } return case *ecdsa.PublicKey: @@ -616,10 +657,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature return err } if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { - return errors.New("crypto/x509: ECDSA signature contained zero or negative values") + return errors.New("x509: ECDSA signature contained zero or negative values") } if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { - return errors.New("crypto/x509: ECDSA verification failure") + return errors.New("x509: ECDSA verification failure") } return } @@ -635,7 +676,7 @@ func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err error) { type UnhandledCriticalExtension struct{} func (h UnhandledCriticalExtension) Error() string { - return "unhandled critical extension" + return "x509: unhandled critical extension" } type basicConstraints struct { @@ -659,6 +700,24 @@ type generalSubtree struct { Name string `asn1:"tag:2,optional,ia5"` } +// RFC 5280, 4.2.2.1 +type authorityInfoAccess struct { + Method asn1.ObjectIdentifier + Location asn1.RawValue +} + +// RFC 5280, 4.2.1.14 +type distributionPoint struct { + DistributionPoint distributionPointName `asn1:"optional,tag:0"` + Reason asn1.BitString `asn1:"optional,tag:1"` + CRLIssuer asn1.RawValue `asn1:"optional,tag:2"` +} + +type distributionPointName struct { + FullName asn1.RawValue `asn1:"optional,tag:0"` + RelativeName pkix.RDNSequence `asn1:"optional,tag:1"` +} + func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{}, error) { asn1Data := keyData.PublicKey.RightAlign() switch algo { @@ -694,7 +753,7 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ return nil, err } if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { - return nil, errors.New("zero or negative DSA parameter") + return nil, errors.New("x509: zero or negative DSA parameter") } pub := &dsa.PublicKey{ Parameters: dsa.Parameters{ @@ -714,11 +773,11 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ } namedCurve := namedCurveFromOID(*namedCurveOID) if namedCurve == nil { - return nil, errors.New("crypto/x509: unsupported elliptic curve") + return nil, errors.New("x509: unsupported elliptic curve") } x, y := elliptic.Unmarshal(namedCurve, asn1Data) if x == nil { - return nil, errors.New("crypto/x509: failed to unmarshal elliptic curve point") + return nil, errors.New("x509: failed to unmarshal elliptic curve point") } pub := &ecdsa.PublicKey{ Curve: namedCurve, @@ -752,7 +811,7 @@ func parseCertificate(in *certificate) (*Certificate, error) { } if in.TBSCertificate.SerialNumber.Sign() < 0 { - return nil, errors.New("negative serial number") + return nil, errors.New("x509: negative serial number") } out.Version = in.TBSCertificate.Version + 1 @@ -773,6 +832,8 @@ func parseCertificate(in *certificate) (*Certificate, error) { out.NotAfter = in.TBSCertificate.Validity.NotAfter for _, e := range in.TBSCertificate.Extensions { + out.Extensions = append(out.Extensions, e) + if len(e.Id) == 4 && e.Id[0] == 2 && e.Id[1] == 5 && e.Id[2] == 29 { switch e.Id[3] { case 15: @@ -896,6 +957,39 @@ func parseCertificate(in *certificate) (*Certificate, error) { } continue + case 31: + // RFC 5280, 4.2.1.14 + + // CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + // + // DistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // reasons [1] ReasonFlags OPTIONAL, + // cRLIssuer [2] GeneralNames OPTIONAL } + // + // DistributionPointName ::= CHOICE { + // fullName [0] GeneralNames, + // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + + var cdp []distributionPoint + _, err := asn1.Unmarshal(e.Value, &cdp) + if err != nil { + return nil, err + } + + for _, dp := range cdp { + var n asn1.RawValue + _, err = asn1.Unmarshal(dp.DistributionPoint.FullName.Bytes, &n) + if err != nil { + return nil, err + } + + if n.Tag == 6 { + out.CRLDistributionPoints = append(out.CRLDistributionPoints, string(n.Bytes)) + } + } + continue + case 35: // RFC 5280, 4.2.1.1 var a authKeyId @@ -952,6 +1046,24 @@ func parseCertificate(in *certificate) (*Certificate, error) { out.PolicyIdentifiers[i] = policy.Policy } } + } else if e.Id.Equal(oidExtensionAuthorityInfoAccess) { + // RFC 5280 4.2.2.1: Authority Information Access + var aia []authorityInfoAccess + if _, err = asn1.Unmarshal(e.Value, &aia); err != nil { + return nil, err + } + + for _, v := range aia { + // GeneralName: uniformResourceIdentifier [6] IA5String + if v.Location.Tag != 6 { + continue + } + if v.Method.Equal(oidAuthorityInfoAccessOcsp) { + out.OCSPServer = append(out.OCSPServer, string(v.Location.Bytes)) + } else if v.Method.Equal(oidAuthorityInfoAccessIssuers) { + out.IssuingCertificateURL = append(out.IssuingCertificateURL, string(v.Location.Bytes)) + } + } } if e.Critical { @@ -1011,21 +1123,40 @@ func reverseBitsInAByte(in byte) byte { } var ( - oidExtensionSubjectKeyId = []int{2, 5, 29, 14} - oidExtensionKeyUsage = []int{2, 5, 29, 15} - oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37} - oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} - oidExtensionBasicConstraints = []int{2, 5, 29, 19} - oidExtensionSubjectAltName = []int{2, 5, 29, 17} - oidExtensionCertificatePolicies = []int{2, 5, 29, 32} - oidExtensionNameConstraints = []int{2, 5, 29, 30} + oidExtensionSubjectKeyId = []int{2, 5, 29, 14} + oidExtensionKeyUsage = []int{2, 5, 29, 15} + oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37} + oidExtensionAuthorityKeyId = []int{2, 5, 29, 35} + oidExtensionBasicConstraints = []int{2, 5, 29, 19} + oidExtensionSubjectAltName = []int{2, 5, 29, 17} + oidExtensionCertificatePolicies = []int{2, 5, 29, 32} + oidExtensionNameConstraints = []int{2, 5, 29, 30} + oidExtensionCRLDistributionPoints = []int{2, 5, 29, 31} + oidExtensionAuthorityInfoAccess = []int{1, 3, 6, 1, 5, 5, 7, 1, 1} +) + +var ( + oidAuthorityInfoAccessOcsp = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1} + oidAuthorityInfoAccessIssuers = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 2} ) +// oidNotInExtensions returns whether an extension with the given oid exists in +// extensions. +func oidInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) bool { + for _, e := range extensions { + if e.Id.Equal(oid) { + return true + } + } + return false +} + func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { - ret = make([]pkix.Extension, 8 /* maximum number of elements. */) + ret = make([]pkix.Extension, 10 /* maximum number of elements. */) n := 0 - if template.KeyUsage != 0 { + if template.KeyUsage != 0 && + !oidInExtensions(oidExtensionKeyUsage, template.ExtraExtensions) { ret[n].Id = oidExtensionKeyUsage ret[n].Critical = true @@ -1045,7 +1176,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0 { + if (len(template.ExtKeyUsage) > 0 || len(template.UnknownExtKeyUsage) > 0) && + !oidInExtensions(oidExtensionExtendedKeyUsage, template.ExtraExtensions) { ret[n].Id = oidExtensionExtendedKeyUsage var oids []asn1.ObjectIdentifier @@ -1066,7 +1198,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if template.BasicConstraintsValid { + if template.BasicConstraintsValid && !oidInExtensions(oidExtensionBasicConstraints, template.ExtraExtensions) { ret[n].Id = oidExtensionBasicConstraints ret[n].Value, err = asn1.Marshal(basicConstraints{template.IsCA, template.MaxPathLen}) ret[n].Critical = true @@ -1076,7 +1208,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if len(template.SubjectKeyId) > 0 { + if len(template.SubjectKeyId) > 0 && !oidInExtensions(oidExtensionSubjectKeyId, template.ExtraExtensions) { ret[n].Id = oidExtensionSubjectKeyId ret[n].Value, err = asn1.Marshal(template.SubjectKeyId) if err != nil { @@ -1085,7 +1217,7 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if len(template.AuthorityKeyId) > 0 { + if len(template.AuthorityKeyId) > 0 && !oidInExtensions(oidExtensionAuthorityKeyId, template.ExtraExtensions) { ret[n].Id = oidExtensionAuthorityKeyId ret[n].Value, err = asn1.Marshal(authKeyId{template.AuthorityKeyId}) if err != nil { @@ -1094,7 +1226,31 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0 { + if (len(template.OCSPServer) > 0 || len(template.IssuingCertificateURL) > 0) && + !oidInExtensions(oidExtensionAuthorityInfoAccess, template.ExtraExtensions) { + ret[n].Id = oidExtensionAuthorityInfoAccess + var aiaValues []authorityInfoAccess + for _, name := range template.OCSPServer { + aiaValues = append(aiaValues, authorityInfoAccess{ + Method: oidAuthorityInfoAccessOcsp, + Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}, + }) + } + for _, name := range template.IssuingCertificateURL { + aiaValues = append(aiaValues, authorityInfoAccess{ + Method: oidAuthorityInfoAccessIssuers, + Location: asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}, + }) + } + ret[n].Value, err = asn1.Marshal(aiaValues) + if err != nil { + return + } + n++ + } + + if (len(template.DNSNames) > 0 || len(template.EmailAddresses) > 0 || len(template.IPAddresses) > 0) && + !oidInExtensions(oidExtensionSubjectAltName, template.ExtraExtensions) { ret[n].Id = oidExtensionSubjectAltName var rawValues []asn1.RawValue for _, name := range template.DNSNames { @@ -1118,7 +1274,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if len(template.PolicyIdentifiers) > 0 { + if len(template.PolicyIdentifiers) > 0 && + !oidInExtensions(oidExtensionCertificatePolicies, template.ExtraExtensions) { ret[n].Id = oidExtensionCertificatePolicies policies := make([]policyInformation, len(template.PolicyIdentifiers)) for i, policy := range template.PolicyIdentifiers { @@ -1131,7 +1288,8 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } - if len(template.PermittedDNSDomains) > 0 { + if len(template.PermittedDNSDomains) > 0 && + !oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) { ret[n].Id = oidExtensionNameConstraints ret[n].Critical = template.PermittedDNSDomainsCritical @@ -1147,10 +1305,33 @@ func buildExtensions(template *Certificate) (ret []pkix.Extension, err error) { n++ } + if len(template.CRLDistributionPoints) > 0 && + !oidInExtensions(oidExtensionCRLDistributionPoints, template.ExtraExtensions) { + ret[n].Id = oidExtensionCRLDistributionPoints + + var crlDp []distributionPoint + for _, name := range template.CRLDistributionPoints { + rawFullName, _ := asn1.Marshal(asn1.RawValue{Tag: 6, Class: 2, Bytes: []byte(name)}) + + dp := distributionPoint{ + DistributionPoint: distributionPointName{ + FullName: asn1.RawValue{Tag: 0, Class: 2, Bytes: rawFullName}, + }, + } + crlDp = append(crlDp, dp) + } + + ret[n].Value, err = asn1.Marshal(crlDp) + if err != nil { + return + } + n++ + } + // Adding another extension here? Remember to update the maximum number // of elements in the make() at the top of the function. - return ret[0:n], nil + return append(ret[:n], template.ExtraExtensions...), nil } func subjectBytes(cert *Certificate) ([]byte, error) { @@ -1179,28 +1360,8 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf var publicKeyBytes []byte var publicKeyAlgorithm pkix.AlgorithmIdentifier - switch pub := pub.(type) { - case *rsa.PublicKey: - publicKeyBytes, err = asn1.Marshal(rsaPublicKey{ - N: pub.N, - E: pub.E, - }) - publicKeyAlgorithm.Algorithm = oidPublicKeyRSA - case *ecdsa.PublicKey: - oid, ok := oidFromNamedCurve(pub.Curve) - if !ok { - return nil, errors.New("x509: unknown elliptic curve") - } - publicKeyAlgorithm.Algorithm = oidPublicKeyECDSA - var paramBytes []byte - paramBytes, err = asn1.Marshal(oid) - if err != nil { - return - } - publicKeyAlgorithm.Parameters.FullBytes = paramBytes - publicKeyBytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y) - default: - return nil, errors.New("x509: only RSA and ECDSA public keys supported") + if publicKeyBytes, publicKeyAlgorithm, err = marshalPublicKey(pub); err != nil { + return nil, err } var signatureAlgorithm pkix.AlgorithmIdentifier diff --git a/libgo/go/crypto/x509/x509_test.go b/libgo/go/crypto/x509/x509_test.go index abd4fe8..f1097e9 100644 --- a/libgo/go/crypto/x509/x509_test.go +++ b/libgo/go/crypto/x509/x509_test.go @@ -237,6 +237,11 @@ func TestCertificateParse(t *testing.T) { if err := certs[0].VerifyHostname("mail.google.com"); err != nil { t.Error(err) } + + const expectedExtensions = 4 + if n := len(certs[0].Extensions); n != expectedExtensions { + t.Errorf("want %d extensions, got %d", expectedExtensions, n) + } } var certBytes = "308203223082028ba00302010202106edf0d9499fd4533dd1297fc42a93be1300d06092a864886" + @@ -308,7 +313,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) { } testExtKeyUsage := []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageServerAuth} - testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{3, 2, 1}} + testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}} + extraExtensionData := []byte("extra extension") for _, test := range tests { commonName := "test.example.com" @@ -330,12 +336,30 @@ func TestCreateSelfSignedCertificate(t *testing.T) { BasicConstraintsValid: true, IsCA: true, + OCSPServer: []string{"http://ocsp.example.com"}, + IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"}, + DNSNames: []string{"test.example.com"}, EmailAddresses: []string{"gopher@golang.org"}, IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")}, PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, PermittedDNSDomains: []string{".example.com", "example.com"}, + + CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"}, + + ExtraExtensions: []pkix.Extension{ + { + Id: []int{1, 2, 3, 4}, + Value: extraExtensionData, + }, + // This extension should override the SubjectKeyId, above. + { + Id: oidExtensionSubjectKeyId, + Critical: false, + Value: []byte{0x04, 0x04, 4, 3, 2, 1}, + }, + }, } derBytes, err := CreateCertificate(random, &template, &template, test.pub, test.priv) @@ -374,6 +398,14 @@ func TestCreateSelfSignedCertificate(t *testing.T) { t.Errorf("%s: unknown extkeyusage wasn't correctly copied from the template. Got %v, want %v", test.name, cert.UnknownExtKeyUsage, testUnknownExtKeyUsage) } + if !reflect.DeepEqual(cert.OCSPServer, template.OCSPServer) { + t.Errorf("%s: OCSP servers differ from template. Got %v, want %v", test.name, cert.OCSPServer, template.OCSPServer) + } + + if !reflect.DeepEqual(cert.IssuingCertificateURL, template.IssuingCertificateURL) { + t.Errorf("%s: Issuing certificate URLs differ from template. Got %v, want %v", test.name, cert.IssuingCertificateURL, template.IssuingCertificateURL) + } + if !reflect.DeepEqual(cert.DNSNames, template.DNSNames) { t.Errorf("%s: SAN DNS names differ from template. Got %v, want %v", test.name, cert.DNSNames, template.DNSNames) } @@ -386,6 +418,18 @@ func TestCreateSelfSignedCertificate(t *testing.T) { t.Errorf("%s: SAN IPs differ from template. Got %v, want %v", test.name, cert.IPAddresses, template.IPAddresses) } + if !reflect.DeepEqual(cert.CRLDistributionPoints, template.CRLDistributionPoints) { + t.Errorf("%s: CRL distribution points differ from template. Got %v, want %v", test.name, cert.CRLDistributionPoints, template.CRLDistributionPoints) + } + + if !bytes.Equal(cert.SubjectKeyId, []byte{4, 3, 2, 1}) { + t.Errorf("%s: ExtraExtensions didn't override SubjectKeyId", test.name) + } + + if bytes.Index(derBytes, extraExtensionData) == -1 { + t.Errorf("%s: didn't find extra extension in DER output", test.name) + } + if test.checkSig { err = cert.CheckSignatureFrom(cert) if err != nil { diff --git a/libgo/go/database/sql/convert_test.go b/libgo/go/database/sql/convert_test.go index 950e24f..a39c2c5 100644 --- a/libgo/go/database/sql/convert_test.go +++ b/libgo/go/database/sql/convert_test.go @@ -267,14 +267,14 @@ func TestValueConverters(t *testing.T) { goterr = err.Error() } if goterr != tt.err { - t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", + t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q", i, tt.c, tt.in, tt.in, goterr, tt.err) } if tt.err != "" { continue } if !reflect.DeepEqual(out, tt.out) { - t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", + t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)", i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) } } diff --git a/libgo/go/database/sql/driver/types_test.go b/libgo/go/database/sql/driver/types_test.go index ab82bca..1ce0ff0 100644 --- a/libgo/go/database/sql/driver/types_test.go +++ b/libgo/go/database/sql/driver/types_test.go @@ -51,14 +51,14 @@ func TestValueConverters(t *testing.T) { goterr = err.Error() } if goterr != tt.err { - t.Errorf("test %d: %s(%T(%v)) error = %q; want error = %q", + t.Errorf("test %d: %T(%T(%v)) error = %q; want error = %q", i, tt.c, tt.in, tt.in, goterr, tt.err) } if tt.err != "" { continue } if !reflect.DeepEqual(out, tt.out) { - t.Errorf("test %d: %s(%T(%v)) = %v (%T); want %v (%T)", + t.Errorf("test %d: %T(%T(%v)) = %v (%T); want %v (%T)", i, tt.c, tt.in, tt.in, out, out, tt.out, tt.out) } } diff --git a/libgo/go/database/sql/fakedb_test.go b/libgo/go/database/sql/fakedb_test.go index d900e2c..a8adfdd 100644 --- a/libgo/go/database/sql/fakedb_test.go +++ b/libgo/go/database/sql/fakedb_test.go @@ -38,6 +38,8 @@ type fakeDriver struct { mu sync.Mutex // guards 3 following fields openCount int // conn opens closeCount int // conn closes + waitCh chan struct{} + waitingCh chan struct{} dbs map[string]*fakeDB } @@ -146,6 +148,12 @@ func (d *fakeDriver) Open(dsn string) (driver.Conn, error) { if len(parts) >= 2 && parts[1] == "badConn" { conn.bad = true } + if d.waitCh != nil { + d.waitingCh <- struct{}{} + <-d.waitCh + d.waitCh = nil + d.waitingCh = nil + } return conn, nil } @@ -447,6 +455,10 @@ func (c *fakeConn) Prepare(query string) (driver.Stmt, error) { return c.prepareCreate(stmt, parts) case "INSERT": return c.prepareInsert(stmt, parts) + case "NOSERT": + // Do all the prep-work like for an INSERT but don't actually insert the row. + // Used for some of the concurrent tests. + return c.prepareInsert(stmt, parts) default: stmt.Close() return nil, errf("unsupported command type %q", cmd) @@ -497,13 +509,20 @@ func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) { } return driver.ResultNoRows, nil case "INSERT": - return s.execInsert(args) + return s.execInsert(args, true) + case "NOSERT": + // Do all the prep-work like for an INSERT but don't actually insert the row. + // Used for some of the concurrent tests. + return s.execInsert(args, false) } fmt.Printf("EXEC statement, cmd=%q: %#v\n", s.cmd, s) return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd) } -func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) { +// When doInsert is true, add the row to the table. +// When doInsert is false do prep-work and error checking, but don't +// actually add the row to the table. +func (s *fakeStmt) execInsert(args []driver.Value, doInsert bool) (driver.Result, error) { db := s.c.db if len(args) != s.placeholders { panic("error in pkg db; should only get here if size is correct") @@ -518,7 +537,10 @@ func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) { t.mu.Lock() defer t.mu.Unlock() - cols := make([]interface{}, len(t.colname)) + var cols []interface{} + if doInsert { + cols = make([]interface{}, len(t.colname)) + } argPos := 0 for n, colname := range s.colName { colidx := t.columnIndex(colname) @@ -532,10 +554,14 @@ func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) { } else { val = s.colValue[n] } - cols[colidx] = val + if doInsert { + cols[colidx] = val + } } - t.rows = append(t.rows, &row{cols: cols}) + if doInsert { + t.rows = append(t.rows, &row{cols: cols}) + } return driver.RowsAffected(1), nil } @@ -608,9 +634,10 @@ rows: } cursor := &rowsCursor{ - pos: -1, - rows: mrows, - cols: s.colName, + pos: -1, + rows: mrows, + cols: s.colName, + errPos: -1, } return cursor, nil } @@ -635,6 +662,10 @@ type rowsCursor struct { rows []*row closed bool + // errPos and err are for making Next return early with error. + errPos int + err error + // a clone of slices to give out to clients, indexed by the // the original slice's first byte address. we clone them // just so we're able to corrupt them on close. @@ -660,6 +691,9 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { return errors.New("fakedb: cursor is closed") } rc.pos++ + if rc.pos == rc.errPos { + return rc.err + } if rc.pos >= len(rc.rows) { return io.EOF // per interface spec } diff --git a/libgo/go/database/sql/sql.go b/libgo/go/database/sql/sql.go index a80782b..f7b4f8c 100644 --- a/libgo/go/database/sql/sql.go +++ b/libgo/go/database/sql/sql.go @@ -7,9 +7,13 @@ // // The sql package must be used in conjunction with a database driver. // See http://golang.org/s/sqldrivers for a list of drivers. +// +// For more usage examples, see the wiki page at +// http://golang.org/s/sqlwiki. package sql import ( + "container/list" "database/sql/driver" "errors" "fmt" @@ -192,12 +196,22 @@ type DB struct { driver driver.Driver dsn string - mu sync.Mutex // protects following fields - freeConn []*driverConn + mu sync.Mutex // protects following fields + freeConn *list.List // of *driverConn + connRequests *list.List // of connRequest + numOpen int + pendingOpens int + // Used to sygnal the need for new connections + // a goroutine running connectionOpener() reads on this chan and + // maybeOpenNewConnections sends on the chan (one send per needed connection) + // It is closed during db.Close(). The close tells the connectionOpener + // goroutine to exit. + openerCh chan struct{} closed bool dep map[finalCloser]depSet lastPut map[*driverConn]string // stacktrace of last conn's put; debug only maxIdle int // zero means defaultMaxIdleConns; negative means 0 + maxOpen int // <= 0 means unlimited } // driverConn wraps a driver.Conn with a mutex, to @@ -217,6 +231,13 @@ type driverConn struct { inUse bool onPut []func() // code (with db.mu held) run when conn is next returned dbmuClosed bool // same as closed, but guarded by db.mu, for connIfFree + // This is the Element returned by db.freeConn.PushFront(conn). + // It's used by connIfFree to remove the conn from the freeConn list. + listElem *list.Element +} + +func (dc *driverConn) releaseConn(err error) { + dc.db.putConn(dc, err) } func (dc *driverConn) removeOpenStmt(si driver.Stmt) { @@ -250,15 +271,14 @@ func (dc *driverConn) prepareLocked(query string) (driver.Stmt, error) { } // the dc.db's Mutex is held. -func (dc *driverConn) closeDBLocked() error { +func (dc *driverConn) closeDBLocked() func() error { dc.Lock() + defer dc.Unlock() if dc.closed { - dc.Unlock() - return errors.New("sql: duplicate driverConn close") + return func() error { return errors.New("sql: duplicate driverConn close") } } dc.closed = true - dc.Unlock() // not defer; removeDep finalClose calls may need to lock - return dc.db.removeDepLocked(dc, dc)() + return dc.db.removeDepLocked(dc, dc) } func (dc *driverConn) Close() error { @@ -289,8 +309,13 @@ func (dc *driverConn) finalClose() error { err := dc.ci.Close() dc.ci = nil dc.finalClosed = true - dc.Unlock() + + dc.db.mu.Lock() + dc.db.numOpen-- + dc.db.maybeOpenNewConnections() + dc.db.mu.Unlock() + return err } @@ -353,26 +378,36 @@ func (db *DB) removeDep(x finalCloser, dep interface{}) error { func (db *DB) removeDepLocked(x finalCloser, dep interface{}) func() error { //println(fmt.Sprintf("removeDep(%T %p, %T %p)", x, x, dep, dep)) - done := false - xdep := db.dep[x] - if xdep != nil { - delete(xdep, dep) - if len(xdep) == 0 { - delete(db.dep, x) - done = true - } + xdep, ok := db.dep[x] + if !ok { + panic(fmt.Sprintf("unpaired removeDep: no deps for %T", x)) } - if !done { + l0 := len(xdep) + delete(xdep, dep) + + switch len(xdep) { + case l0: + // Nothing removed. Shouldn't happen. + panic(fmt.Sprintf("unpaired removeDep: no %T dep on %T", dep, x)) + case 0: + // No more dependencies. + delete(db.dep, x) + return x.finalClose + default: + // Dependencies remain. return func() error { return nil } } - return func() error { - //println(fmt.Sprintf("calling final close on %T %v (%#v)", x, x, x)) - return x.finalClose() - } } +// This is the size of the connectionOpener request chan (dn.openerCh). +// 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. +var connectionRequestQueueSize = 1000000 + // Open opens a database specified by its database driver name and a // driver-specific data source name, usually consisting of at least a // database name and connection information. @@ -391,10 +426,14 @@ func Open(driverName, dataSourceName string) (*DB, error) { return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName) } db := &DB{ - driver: driveri, - dsn: dataSourceName, - lastPut: make(map[*driverConn]string), - } + driver: driveri, + dsn: dataSourceName, + openerCh: make(chan struct{}, connectionRequestQueueSize), + lastPut: make(map[*driverConn]string), + } + db.freeConn = list.New() + db.connRequests = list.New() + go db.connectionOpener() return db, nil } @@ -415,16 +454,32 @@ func (db *DB) Ping() error { // Close closes the database, releasing any open resources. func (db *DB) Close() error { db.mu.Lock() - defer db.mu.Unlock() + if db.closed { // Make DB.Close idempotent + db.mu.Unlock() + return nil + } + close(db.openerCh) var err error - for _, dc := range db.freeConn { - err1 := dc.closeDBLocked() + fns := make([]func() error, 0, db.freeConn.Len()) + for db.freeConn.Front() != nil { + dc := db.freeConn.Front().Value.(*driverConn) + dc.listElem = nil + fns = append(fns, dc.closeDBLocked()) + db.freeConn.Remove(db.freeConn.Front()) + } + db.closed = true + for db.connRequests.Front() != nil { + req := db.connRequests.Front().Value.(connRequest) + db.connRequests.Remove(db.connRequests.Front()) + close(req) + } + db.mu.Unlock() + for _, fn := range fns { + err1 := fn() if err1 != nil { err = err1 } } - db.freeConn = nil - db.closed = true return err } @@ -446,50 +501,168 @@ func (db *DB) maxIdleConnsLocked() int { // SetMaxIdleConns sets the maximum number of connections in the idle // connection pool. // +// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns +// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit +// // If n <= 0, no idle connections are retained. func (db *DB) SetMaxIdleConns(n int) { db.mu.Lock() - defer db.mu.Unlock() if n > 0 { db.maxIdle = n } else { // No idle connections. db.maxIdle = -1 } - for len(db.freeConn) > 0 && len(db.freeConn) > n { - nfree := len(db.freeConn) - dc := db.freeConn[nfree-1] - db.freeConn[nfree-1] = nil - db.freeConn = db.freeConn[:nfree-1] - go dc.Close() + // Make sure maxIdle doesn't exceed maxOpen + if db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen { + db.maxIdle = db.maxOpen + } + var closing []*driverConn + for db.freeConn.Len() > db.maxIdleConnsLocked() { + dc := db.freeConn.Back().Value.(*driverConn) + dc.listElem = nil + db.freeConn.Remove(db.freeConn.Back()) + closing = append(closing, dc) + } + db.mu.Unlock() + for _, c := range closing { + c.Close() } } +// SetMaxOpenConns sets the maximum number of open connections to the database. +// +// If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than +// MaxIdleConns, then MaxIdleConns will be reduced to match the new +// MaxOpenConns limit +// +// If n <= 0, then there is no limit on the number of open connections. +// The default is 0 (unlimited). +func (db *DB) SetMaxOpenConns(n int) { + db.mu.Lock() + db.maxOpen = n + if n < 0 { + db.maxOpen = 0 + } + syncMaxIdle := db.maxOpen > 0 && db.maxIdleConnsLocked() > db.maxOpen + db.mu.Unlock() + if syncMaxIdle { + db.SetMaxIdleConns(n) + } +} + +// Assumes db.mu is locked. +// If there are connRequests and the connection limit hasn't been reached, +// then tell the connectionOpener to open new connections. +func (db *DB) maybeOpenNewConnections() { + numRequests := db.connRequests.Len() - db.pendingOpens + if db.maxOpen > 0 { + numCanOpen := db.maxOpen - (db.numOpen + db.pendingOpens) + if numRequests > numCanOpen { + numRequests = numCanOpen + } + } + for numRequests > 0 { + db.pendingOpens++ + numRequests-- + db.openerCh <- struct{}{} + } +} + +// Runs in a seperate goroutine, opens new connections when requested. +func (db *DB) connectionOpener() { + for _ = range db.openerCh { + db.openNewConnection() + } +} + +// Open one new connection +func (db *DB) openNewConnection() { + ci, err := db.driver.Open(db.dsn) + db.mu.Lock() + defer db.mu.Unlock() + if db.closed { + if err == nil { + ci.Close() + } + return + } + db.pendingOpens-- + if err != nil { + db.putConnDBLocked(nil, err) + return + } + dc := &driverConn{ + db: db, + ci: ci, + } + if db.putConnDBLocked(dc, err) { + db.addDepLocked(dc, dc) + db.numOpen++ + } else { + ci.Close() + } +} + +// connRequest represents one request for a new connection +// When there are no idle connections available, DB.conn will create +// a new connRequest and put it on the db.connRequests list. +type connRequest chan<- interface{} // takes either a *driverConn or an error + +var errDBClosed = errors.New("sql: database is closed") + // conn returns a newly-opened or cached *driverConn func (db *DB) conn() (*driverConn, error) { db.mu.Lock() if db.closed { db.mu.Unlock() - return nil, errors.New("sql: database is closed") + return nil, errDBClosed + } + + // If db.maxOpen > 0 and the number of open connections is over the limit + // or there are no free connection, then make a request and wait. + if db.maxOpen > 0 && (db.numOpen >= db.maxOpen || db.freeConn.Len() == 0) { + // Make the connRequest channel. It's buffered so that the + // connectionOpener doesn't block while waiting for the req to be read. + ch := make(chan interface{}, 1) + req := connRequest(ch) + db.connRequests.PushBack(req) + db.maybeOpenNewConnections() + db.mu.Unlock() + ret, ok := <-ch + if !ok { + return nil, errDBClosed + } + switch ret.(type) { + case *driverConn: + return ret.(*driverConn), nil + case error: + return nil, ret.(error) + default: + panic("sql: Unexpected type passed through connRequest.ch") + } } - if n := len(db.freeConn); n > 0 { - conn := db.freeConn[n-1] - db.freeConn = db.freeConn[:n-1] + + if f := db.freeConn.Front(); f != nil { + conn := f.Value.(*driverConn) + conn.listElem = nil + db.freeConn.Remove(f) conn.inUse = true db.mu.Unlock() return conn, nil } - db.mu.Unlock() + db.mu.Unlock() ci, err := db.driver.Open(db.dsn) if err != nil { return nil, err } + db.mu.Lock() + db.numOpen++ dc := &driverConn{ db: db, ci: ci, } - db.mu.Lock() db.addDepLocked(dc, dc) dc.inUse = true db.mu.Unlock() @@ -511,18 +684,15 @@ var ( func (db *DB) connIfFree(wanted *driverConn) (*driverConn, error) { db.mu.Lock() defer db.mu.Unlock() - if wanted.inUse { - return nil, errConnBusy - } if wanted.dbmuClosed { return nil, errConnClosed } - for i, conn := range db.freeConn { - if conn != wanted { - continue - } - db.freeConn[i] = db.freeConn[len(db.freeConn)-1] - db.freeConn = db.freeConn[:len(db.freeConn)-1] + if wanted.inUse { + return nil, errConnBusy + } + if wanted.listElem != nil { + db.freeConn.Remove(wanted.listElem) + wanted.listElem = nil wanted.inUse = true return wanted, nil } @@ -582,20 +752,50 @@ func (db *DB) putConn(dc *driverConn, err error) { if err == driver.ErrBadConn { // Don't reuse bad connections. + // Since the conn is considered bad and is being discarded, treat it + // as closed. Don't decrement the open count here, finalClose will + // take care of that. + db.maybeOpenNewConnections() db.mu.Unlock() + dc.Close() return } if putConnHook != nil { putConnHook(db, dc) } - if n := len(db.freeConn); !db.closed && n < db.maxIdleConnsLocked() { - db.freeConn = append(db.freeConn, dc) - db.mu.Unlock() - return - } + added := db.putConnDBLocked(dc, nil) db.mu.Unlock() - dc.Close() + if !added { + dc.Close() + } +} + +// Satisfy a connRequest or put the driverConn in the idle pool and return true +// or return false. +// putConnDBLocked will satisfy a connRequest if there is one, or it will +// return the *driverConn to the freeConn list if err != nil and the idle +// connection limit would not be reached. +// 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 +// freeConn list, then true is returned, otherwise false is returned. +func (db *DB) putConnDBLocked(dc *driverConn, err error) bool { + if db.connRequests.Len() > 0 { + req := db.connRequests.Front().Value.(connRequest) + db.connRequests.Remove(db.connRequests.Front()) + if err != nil { + req <- err + } else { + dc.inUse = true + req <- dc + } + return true + } else if err == nil && !db.closed && db.maxIdleConnsLocked() > 0 && db.maxIdleConnsLocked() > db.freeConn.Len() { + dc.listElem = db.freeConn.PushFront(dc) + return true + } + return false } // Prepare creates a prepared statement for later queries or executions. @@ -710,9 +910,7 @@ func (db *DB) query(query string, args []interface{}) (*Rows, error) { return nil, err } - releaseConn := func(err error) { db.putConn(ci, err) } - - return db.queryConn(ci, releaseConn, query, args) + return db.queryConn(ci, ci.releaseConn, query, args) } // queryConn executes a query on the given connection. @@ -754,10 +952,10 @@ func (db *DB) queryConn(dc *driverConn, releaseConn func(error), query string, a ds := driverStmt{dc, si} rowsi, err := rowsiFromStatement(ds, args...) if err != nil { - releaseConn(err) dc.Lock() si.Close() dc.Unlock() + releaseConn(err) return nil, err } @@ -1154,8 +1352,7 @@ func (s *Stmt) connStmt() (ci *driverConn, releaseConn func(error), si driver.St } conn := cs.dc - releaseConn = func(err error) { s.db.putConn(conn, err) } - return conn, releaseConn, cs.si, nil + return conn, conn.releaseConn, cs.si, nil } // Query executes a prepared query statement with the given arguments @@ -1245,27 +1442,32 @@ func (s *Stmt) Close() error { return s.stickyErr } s.mu.Lock() - defer s.mu.Unlock() if s.closed { + s.mu.Unlock() return nil } s.closed = true if s.tx != nil { s.txsi.Close() + s.mu.Unlock() return nil } + s.mu.Unlock() return s.db.removeDep(s, s) } func (s *Stmt) finalClose() error { - for _, v := range s.css { - s.db.noteUnusedDriverStatement(v.dc, v.si) - v.dc.removeOpenStmt(v.si) - s.db.removeDep(v.dc, s) + s.mu.Lock() + defer s.mu.Unlock() + if s.css != nil { + for _, v := range s.css { + s.db.noteUnusedDriverStatement(v.dc, v.si) + v.dc.removeOpenStmt(v.si) + } + s.css = nil } - s.css = nil return nil } @@ -1289,7 +1491,7 @@ type Rows struct { closed bool lastcols []driver.Value - lasterr error + lasterr error // non-nil only if closed is true closeStmt driver.Stmt // if non-nil, statement to Close on close } @@ -1301,20 +1503,19 @@ func (rs *Rows) Next() bool { if rs.closed { return false } - if rs.lasterr != nil { - return false - } if rs.lastcols == nil { rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns())) } rs.lasterr = rs.rowsi.Next(rs.lastcols) - if rs.lasterr == io.EOF { + if rs.lasterr != nil { rs.Close() + return false } - return rs.lasterr == nil + return true } // Err returns the error, if any, that was encountered during iteration. +// Err may be called after an explicit or implicit Close. func (rs *Rows) Err() error { if rs.lasterr == io.EOF { return nil @@ -1349,10 +1550,7 @@ func (rs *Rows) Columns() ([]string, error) { // is of type []byte, a copy is made and the caller owns the result. func (rs *Rows) Scan(dest ...interface{}) error { if rs.closed { - return errors.New("sql: Rows closed") - } - if rs.lasterr != nil { - return rs.lasterr + return errors.New("sql: Rows are closed") } if rs.lastcols == nil { return errors.New("sql: Scan called without calling Next") @@ -1369,15 +1567,20 @@ func (rs *Rows) Scan(dest ...interface{}) error { return nil } -// Close closes the Rows, preventing further enumeration. If the -// end is encountered, the Rows are closed automatically. Close -// is idempotent. +var rowsCloseHook func(*Rows, *error) + +// Close closes the Rows, preventing further enumeration. If Next returns +// false, the Rows are closed automatically and it will suffice to check the +// result of Err. Close is idempotent and does not affect the result of Err. func (rs *Rows) Close() error { if rs.closed { return nil } rs.closed = true err := rs.rowsi.Close() + if fn := rowsCloseHook; fn != nil { + fn(rs, &err) + } if rs.closeStmt != nil { rs.closeStmt.Close() } @@ -1414,13 +1617,13 @@ func (r *Row) Scan(dest ...interface{}) error { // from Next will not be modified again." (for instance, if // they were obtained from the network anyway) But for now we // don't care. + defer r.rows.Close() for _, dp := range dest { if _, ok := dp.(*RawBytes); ok { return errors.New("sql: RawBytes isn't allowed on Row.Scan") } } - defer r.rows.Close() if !r.rows.Next() { return ErrNoRows } diff --git a/libgo/go/database/sql/sql_test.go b/libgo/go/database/sql/sql_test.go index e6cc667..093c0d6 100644 --- a/libgo/go/database/sql/sql_test.go +++ b/libgo/go/database/sql/sql_test.go @@ -5,7 +5,10 @@ package sql import ( + "database/sql/driver" + "errors" "fmt" + "math/rand" "reflect" "runtime" "strings" @@ -21,14 +24,12 @@ func init() { } freedFrom := make(map[dbConn]string) putConnHook = func(db *DB, c *driverConn) { - for _, oc := range db.freeConn { - if oc == c { - // print before panic, as panic may get lost due to conflicting panic - // (all goroutines asleep) elsewhere, since we might not unlock - // the mutex in freeConn here. - println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) - panic("double free of conn.") - } + if c.listElem != nil { + // print before panic, as panic may get lost due to conflicting panic + // (all goroutines asleep) elsewhere, since we might not unlock + // the mutex in freeConn here. + println("double free of conn. conflicts are:\nA) " + freedFrom[dbConn{db, c}] + "\n\nand\nB) " + stack()) + panic("double free of conn.") } freedFrom[dbConn{db, c}] = stack() } @@ -38,15 +39,7 @@ const fakeDBName = "foo" var chrisBirthday = time.Unix(123456789, 0) -type testOrBench interface { - Fatalf(string, ...interface{}) - Errorf(string, ...interface{}) - Fatal(...interface{}) - Error(...interface{}) - Logf(string, ...interface{}) -} - -func newTestDB(t testOrBench, name string) *DB { +func newTestDB(t testing.TB, name string) *DB { db, err := Open("test", fakeDBName) if err != nil { t.Fatalf("Open: %v", err) @@ -68,14 +61,14 @@ func newTestDB(t testOrBench, name string) *DB { return db } -func exec(t testOrBench, db *DB, query string, args ...interface{}) { +func exec(t testing.TB, db *DB, query string, args ...interface{}) { _, err := db.Exec(query, args...) if err != nil { t.Fatalf("Exec of %q: %v", query, err) } } -func closeDB(t testOrBench, db *DB) { +func closeDB(t testing.TB, db *DB) { if e := recover(); e != nil { fmt.Printf("Panic: %v\n", e) panic(e) @@ -86,29 +79,36 @@ func closeDB(t testOrBench, db *DB) { t.Errorf("Error closing fakeConn: %v", err) } }) - for i, dc := range db.freeConn { + for node, i := db.freeConn.Front(), 0; node != nil; node, i = node.Next(), i+1 { + dc := node.Value.(*driverConn) if n := len(dc.openStmt); n > 0 { // Just a sanity check. This is legal in // general, but if we make the tests clean up // their statements first, then we can safely // verify this is always zero here, and any // other value is a leak. - t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, len(db.freeConn), n) + t.Errorf("while closing db, freeConn %d/%d had %d open stmts; want 0", i, db.freeConn.Len(), n) } } err := db.Close() if err != nil { t.Fatalf("error closing DB: %v", err) } + db.mu.Lock() + count := db.numOpen + db.mu.Unlock() + if count != 0 { + t.Fatalf("%d connections still open after closing DB", db.numOpen) + } } // numPrepares assumes that db has exactly 1 idle conn and returns // its count of calls to Prepare func numPrepares(t *testing.T, db *DB) int { - if n := len(db.freeConn); n != 1 { + if n := db.freeConn.Len(); n != 1 { t.Fatalf("free conns = %d; want 1", n) } - return db.freeConn[0].ci.(*fakeConn).numPrepare + return (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn).numPrepare } func (db *DB) numDeps() int { @@ -133,7 +133,7 @@ func (db *DB) numDepsPollUntil(want int, d time.Duration) int { func (db *DB) numFreeConns() int { db.mu.Lock() defer db.mu.Unlock() - return len(db.freeConn) + return db.freeConn.Len() } func (db *DB) dumpDeps(t *testing.T) { @@ -252,6 +252,9 @@ func TestRowsColumns(t *testing.T) { if !reflect.DeepEqual(cols, want) { t.Errorf("got %#v; want %#v", cols, want) } + if err := rows.Close(); err != nil { + t.Errorf("error closing rows: %s", err) + } } func TestQueryRow(t *testing.T) { @@ -648,10 +651,10 @@ func TestQueryRowClosingStmt(t *testing.T) { if err != nil { t.Fatal(err) } - if len(db.freeConn) != 1 { + if db.freeConn.Len() != 1 { t.Fatalf("expected 1 free conn") } - fakeConn := db.freeConn[0].ci.(*fakeConn) + fakeConn := (db.freeConn.Front().Value.(*driverConn)).ci.(*fakeConn) if made, closed := fakeConn.stmtsMade, fakeConn.stmtsClosed; made != closed { t.Errorf("statement close mismatch: made %d, closed %d", made, closed) } @@ -847,13 +850,13 @@ func TestMaxIdleConns(t *testing.T) { t.Fatal(err) } tx.Commit() - if got := len(db.freeConn); got != 1 { + if got := db.freeConn.Len(); got != 1 { t.Errorf("freeConns = %d; want 1", got) } db.SetMaxIdleConns(0) - if got := len(db.freeConn); got != 0 { + if got := db.freeConn.Len(); got != 0 { t.Errorf("freeConns after set to zero = %d; want 0", got) } @@ -862,11 +865,146 @@ func TestMaxIdleConns(t *testing.T) { t.Fatal(err) } tx.Commit() - if got := len(db.freeConn); got != 0 { + if got := db.freeConn.Len(); got != 0 { t.Errorf("freeConns = %d; want 0", got) } } +func TestMaxOpenConns(t *testing.T) { + if testing.Short() { + t.Skip("skipping in short mode") + } + defer setHookpostCloseConn(nil) + setHookpostCloseConn(func(_ *fakeConn, err error) { + if err != nil { + t.Errorf("Error closing fakeConn: %v", err) + } + }) + + db := newTestDB(t, "magicquery") + defer closeDB(t, db) + + driver := db.driver.(*fakeDriver) + + // Force the number of open connections to 0 so we can get an accurate + // count for the test + db.SetMaxIdleConns(0) + + if g, w := db.numFreeConns(), 0; g != w { + t.Errorf("free conns = %d; want %d", g, w) + } + + if n := db.numDepsPollUntil(0, time.Second); n > 0 { + t.Errorf("number of dependencies = %d; expected 0", n) + db.dumpDeps(t) + } + + driver.mu.Lock() + opens0 := driver.openCount + closes0 := driver.closeCount + driver.mu.Unlock() + + db.SetMaxIdleConns(10) + db.SetMaxOpenConns(10) + + stmt, err := db.Prepare("SELECT|magicquery|op|op=?,millis=?") + if err != nil { + t.Fatal(err) + } + + // Start 50 parallel slow queries. + const ( + nquery = 50 + sleepMillis = 25 + nbatch = 2 + ) + var wg sync.WaitGroup + for batch := 0; batch < nbatch; batch++ { + for i := 0; i < nquery; i++ { + wg.Add(1) + go func() { + defer wg.Done() + var op string + if err := stmt.QueryRow("sleep", sleepMillis).Scan(&op); err != nil && err != ErrNoRows { + t.Error(err) + } + }() + } + // Sleep for twice the expected length of time for the + // batch of 50 queries above to finish before starting + // the next round. + time.Sleep(2 * sleepMillis * time.Millisecond) + } + wg.Wait() + + if g, w := db.numFreeConns(), 10; g != w { + t.Errorf("free conns = %d; want %d", g, w) + } + + if n := db.numDepsPollUntil(20, time.Second); n > 20 { + t.Errorf("number of dependencies = %d; expected <= 20", n) + db.dumpDeps(t) + } + + driver.mu.Lock() + opens := driver.openCount - opens0 + closes := driver.closeCount - closes0 + driver.mu.Unlock() + + if opens > 10 { + t.Logf("open calls = %d", opens) + t.Logf("close calls = %d", closes) + t.Errorf("db connections opened = %d; want <= 10", opens) + db.dumpDeps(t) + } + + if err := stmt.Close(); err != nil { + t.Fatal(err) + } + + if g, w := db.numFreeConns(), 10; g != w { + t.Errorf("free conns = %d; want %d", g, w) + } + + if n := db.numDepsPollUntil(10, time.Second); n > 10 { + t.Errorf("number of dependencies = %d; expected <= 10", n) + db.dumpDeps(t) + } + + db.SetMaxOpenConns(5) + + if g, w := db.numFreeConns(), 5; g != w { + t.Errorf("free conns = %d; want %d", g, w) + } + + if n := db.numDepsPollUntil(5, time.Second); n > 5 { + t.Errorf("number of dependencies = %d; expected 0", n) + db.dumpDeps(t) + } + + db.SetMaxOpenConns(0) + + if g, w := db.numFreeConns(), 5; g != w { + t.Errorf("free conns = %d; want %d", g, w) + } + + if n := db.numDepsPollUntil(5, time.Second); n > 5 { + t.Errorf("number of dependencies = %d; expected 0", n) + db.dumpDeps(t) + } + + db.SetMaxIdleConns(0) + + if g, w := db.numFreeConns(), 0; g != w { + t.Errorf("free conns = %d; want %d", g, w) + } + + if n := db.numDepsPollUntil(0, time.Second); n > 0 { + t.Errorf("number of dependencies = %d; expected 0", n) + db.dumpDeps(t) + } +} + // golang.org/issue/5323 func TestStmtCloseDeps(t *testing.T) { if testing.Short() { @@ -932,8 +1070,8 @@ func TestStmtCloseDeps(t *testing.T) { driver.mu.Lock() opens := driver.openCount - opens0 closes := driver.closeCount - closes0 - driver.mu.Unlock() openDelta := (driver.openCount - driver.closeCount) - openDelta0 + driver.mu.Unlock() if openDelta > 2 { t.Logf("open calls = %d", opens) @@ -991,10 +1129,10 @@ func TestCloseConnBeforeStmts(t *testing.T) { t.Fatal(err) } - if len(db.freeConn) != 1 { - t.Fatalf("expected 1 freeConn; got %d", len(db.freeConn)) + if db.freeConn.Len() != 1 { + t.Fatalf("expected 1 freeConn; got %d", db.freeConn.Len()) } - dc := db.freeConn[0] + dc := db.freeConn.Front().Value.(*driverConn) if dc.closed { t.Errorf("conn shouldn't be closed") } @@ -1046,7 +1184,393 @@ func TestRowsCloseOrder(t *testing.T) { } } -func manyConcurrentQueries(t testOrBench) { +func TestRowsImplicitClose(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + rows, err := db.Query("SELECT|people|age,name|") + if err != nil { + t.Fatal(err) + } + + want, fail := 2, errors.New("fail") + r := rows.rowsi.(*rowsCursor) + r.errPos, r.err = want, fail + + got := 0 + for rows.Next() { + got++ + } + if got != want { + t.Errorf("got %d rows, want %d", got, want) + } + if err := rows.Err(); err != fail { + t.Errorf("got error %v, want %v", err, fail) + } + if !r.closed { + t.Errorf("r.closed is false, want true") + } +} + +func TestStmtCloseOrder(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + db.SetMaxIdleConns(0) + setStrictFakeConnClose(t) + defer setStrictFakeConnClose(nil) + + _, err := db.Query("SELECT|non_existent|name|") + if err == nil { + t.Fatal("Quering non-existent table should fail") + } +} + +type concurrentTest interface { + init(t testing.TB, db *DB) + finish(t testing.TB) + test(t testing.TB) error +} + +type concurrentDBQueryTest struct { + db *DB +} + +func (c *concurrentDBQueryTest) init(t testing.TB, db *DB) { + c.db = db +} + +func (c *concurrentDBQueryTest) finish(t testing.TB) { + c.db = nil +} + +func (c *concurrentDBQueryTest) test(t testing.TB) error { + rows, err := c.db.Query("SELECT|people|name|") + if err != nil { + t.Error(err) + return err + } + var name string + for rows.Next() { + rows.Scan(&name) + } + rows.Close() + return nil +} + +type concurrentDBExecTest struct { + db *DB +} + +func (c *concurrentDBExecTest) init(t testing.TB, db *DB) { + c.db = db +} + +func (c *concurrentDBExecTest) finish(t testing.TB) { + c.db = nil +} + +func (c *concurrentDBExecTest) test(t testing.TB) error { + _, err := c.db.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) + if err != nil { + t.Error(err) + return err + } + return nil +} + +type concurrentStmtQueryTest struct { + db *DB + stmt *Stmt +} + +func (c *concurrentStmtQueryTest) init(t testing.TB, db *DB) { + c.db = db + var err error + c.stmt, err = db.Prepare("SELECT|people|name|") + if err != nil { + t.Fatal(err) + } +} + +func (c *concurrentStmtQueryTest) finish(t testing.TB) { + if c.stmt != nil { + c.stmt.Close() + c.stmt = nil + } + c.db = nil +} + +func (c *concurrentStmtQueryTest) test(t testing.TB) error { + rows, err := c.stmt.Query() + if err != nil { + t.Errorf("error on query: %v", err) + return err + } + + var name string + for rows.Next() { + rows.Scan(&name) + } + rows.Close() + return nil +} + +type concurrentStmtExecTest struct { + db *DB + stmt *Stmt +} + +func (c *concurrentStmtExecTest) init(t testing.TB, db *DB) { + c.db = db + var err error + c.stmt, err = db.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?") + if err != nil { + t.Fatal(err) + } +} + +func (c *concurrentStmtExecTest) finish(t testing.TB) { + if c.stmt != nil { + c.stmt.Close() + c.stmt = nil + } + c.db = nil +} + +func (c *concurrentStmtExecTest) test(t testing.TB) error { + _, err := c.stmt.Exec(3, chrisBirthday) + if err != nil { + t.Errorf("error on exec: %v", err) + return err + } + return nil +} + +type concurrentTxQueryTest struct { + db *DB + tx *Tx +} + +func (c *concurrentTxQueryTest) init(t testing.TB, db *DB) { + c.db = db + var err error + c.tx, err = c.db.Begin() + if err != nil { + t.Fatal(err) + } +} + +func (c *concurrentTxQueryTest) finish(t testing.TB) { + if c.tx != nil { + c.tx.Rollback() + c.tx = nil + } + c.db = nil +} + +func (c *concurrentTxQueryTest) test(t testing.TB) error { + rows, err := c.db.Query("SELECT|people|name|") + if err != nil { + t.Error(err) + return err + } + var name string + for rows.Next() { + rows.Scan(&name) + } + rows.Close() + return nil +} + +type concurrentTxExecTest struct { + db *DB + tx *Tx +} + +func (c *concurrentTxExecTest) init(t testing.TB, db *DB) { + c.db = db + var err error + c.tx, err = c.db.Begin() + if err != nil { + t.Fatal(err) + } +} + +func (c *concurrentTxExecTest) finish(t testing.TB) { + if c.tx != nil { + c.tx.Rollback() + c.tx = nil + } + c.db = nil +} + +func (c *concurrentTxExecTest) test(t testing.TB) error { + _, err := c.tx.Exec("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?", 3, chrisBirthday) + if err != nil { + t.Error(err) + return err + } + return nil +} + +type concurrentTxStmtQueryTest struct { + db *DB + tx *Tx + stmt *Stmt +} + +func (c *concurrentTxStmtQueryTest) init(t testing.TB, db *DB) { + c.db = db + var err error + c.tx, err = c.db.Begin() + if err != nil { + t.Fatal(err) + } + c.stmt, err = c.tx.Prepare("SELECT|people|name|") + if err != nil { + t.Fatal(err) + } +} + +func (c *concurrentTxStmtQueryTest) finish(t testing.TB) { + if c.stmt != nil { + c.stmt.Close() + c.stmt = nil + } + if c.tx != nil { + c.tx.Rollback() + c.tx = nil + } + c.db = nil +} + +func (c *concurrentTxStmtQueryTest) test(t testing.TB) error { + rows, err := c.stmt.Query() + if err != nil { + t.Errorf("error on query: %v", err) + return err + } + + var name string + for rows.Next() { + rows.Scan(&name) + } + rows.Close() + return nil +} + +type concurrentTxStmtExecTest struct { + db *DB + tx *Tx + stmt *Stmt +} + +func (c *concurrentTxStmtExecTest) init(t testing.TB, db *DB) { + c.db = db + var err error + c.tx, err = c.db.Begin() + if err != nil { + t.Fatal(err) + } + c.stmt, err = c.tx.Prepare("NOSERT|people|name=Chris,age=?,photo=CPHOTO,bdate=?") + if err != nil { + t.Fatal(err) + } +} + +func (c *concurrentTxStmtExecTest) finish(t testing.TB) { + if c.stmt != nil { + c.stmt.Close() + c.stmt = nil + } + if c.tx != nil { + c.tx.Rollback() + c.tx = nil + } + c.db = nil +} + +func (c *concurrentTxStmtExecTest) test(t testing.TB) error { + _, err := c.stmt.Exec(3, chrisBirthday) + if err != nil { + t.Errorf("error on exec: %v", err) + return err + } + return nil +} + +type concurrentRandomTest struct { + tests []concurrentTest +} + +func (c *concurrentRandomTest) init(t testing.TB, db *DB) { + c.tests = []concurrentTest{ + new(concurrentDBQueryTest), + new(concurrentDBExecTest), + new(concurrentStmtQueryTest), + new(concurrentStmtExecTest), + new(concurrentTxQueryTest), + new(concurrentTxExecTest), + new(concurrentTxStmtQueryTest), + new(concurrentTxStmtExecTest), + } + for _, ct := range c.tests { + ct.init(t, db) + } +} + +func (c *concurrentRandomTest) finish(t testing.TB) { + for _, ct := range c.tests { + ct.finish(t) + } +} + +func (c *concurrentRandomTest) test(t testing.TB) error { + ct := c.tests[rand.Intn(len(c.tests))] + return ct.test(t) +} + +func doConcurrentTest(t testing.TB, ct concurrentTest) { + maxProcs, numReqs := 1, 500 + if testing.Short() { + maxProcs, numReqs = 4, 50 + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs)) + + db := newTestDB(t, "people") + defer closeDB(t, db) + + ct.init(t, db) + defer ct.finish(t) + + var wg sync.WaitGroup + wg.Add(numReqs) + + reqs := make(chan bool) + defer close(reqs) + + for i := 0; i < maxProcs*2; i++ { + go func() { + for _ = range reqs { + err := ct.test(t) + if err != nil { + wg.Done() + continue + } + wg.Done() + } + }() + } + + for i := 0; i < numReqs; i++ { + reqs <- true + } + + wg.Wait() +} + +func manyConcurrentQueries(t testing.TB) { maxProcs, numReqs := 16, 500 if testing.Short() { maxProcs, numReqs = 4, 50 @@ -1096,13 +1620,174 @@ func manyConcurrentQueries(t testOrBench) { wg.Wait() } +func TestIssue6081(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + + drv := db.driver.(*fakeDriver) + drv.mu.Lock() + opens0 := drv.openCount + closes0 := drv.closeCount + drv.mu.Unlock() + + stmt, err := db.Prepare("SELECT|people|name|") + if err != nil { + t.Fatal(err) + } + rowsCloseHook = func(rows *Rows, err *error) { + *err = driver.ErrBadConn + } + defer func() { rowsCloseHook = nil }() + for i := 0; i < 10; i++ { + rows, err := stmt.Query() + if err != nil { + t.Fatal(err) + } + rows.Close() + } + if n := len(stmt.css); n > 1 { + t.Errorf("len(css slice) = %d; want <= 1", n) + } + stmt.Close() + if n := len(stmt.css); n != 0 { + t.Errorf("len(css slice) after Close = %d; want 0", n) + } + + drv.mu.Lock() + opens := drv.openCount - opens0 + closes := drv.closeCount - closes0 + drv.mu.Unlock() + if opens < 9 { + t.Errorf("opens = %d; want >= 9", opens) + } + if closes < 9 { + t.Errorf("closes = %d; want >= 9", closes) + } +} + func TestConcurrency(t *testing.T) { - manyConcurrentQueries(t) + doConcurrentTest(t, new(concurrentDBQueryTest)) + doConcurrentTest(t, new(concurrentDBExecTest)) + doConcurrentTest(t, new(concurrentStmtQueryTest)) + doConcurrentTest(t, new(concurrentStmtExecTest)) + doConcurrentTest(t, new(concurrentTxQueryTest)) + doConcurrentTest(t, new(concurrentTxExecTest)) + doConcurrentTest(t, new(concurrentTxStmtQueryTest)) + doConcurrentTest(t, new(concurrentTxStmtExecTest)) + doConcurrentTest(t, new(concurrentRandomTest)) +} + +func TestConnectionLeak(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) + // Start by opening defaultMaxIdleConns + rows := make([]*Rows, defaultMaxIdleConns) + // We need to SetMaxOpenConns > MaxIdleConns, so the DB can open + // a new connection and we can fill the idle queue with the released + // connections. + db.SetMaxOpenConns(len(rows) + 1) + for ii := range rows { + r, err := db.Query("SELECT|people|name|") + if err != nil { + t.Fatal(err) + } + r.Next() + if err := r.Err(); err != nil { + t.Fatal(err) + } + rows[ii] = r + } + // Now we have defaultMaxIdleConns busy connections. Open + // a new one, but wait until the busy connections are released + // before returning control to DB. + drv := db.driver.(*fakeDriver) + drv.waitCh = make(chan struct{}, 1) + drv.waitingCh = make(chan struct{}, 1) + var wg sync.WaitGroup + wg.Add(1) + go func() { + r, err := db.Query("SELECT|people|name|") + if err != nil { + t.Fatal(err) + } + r.Close() + wg.Done() + }() + // Wait until the goroutine we've just created has started waiting. + <-drv.waitingCh + // Now close the busy connections. This provides a connection for + // the blocked goroutine and then fills up the idle queue. + for _, v := range rows { + v.Close() + } + // At this point we give the new connection to DB. This connection is + // now useless, since the idle queue is full and there are no pending + // requests. DB should deal with this situation without leaking the + // connection. + drv.waitCh <- struct{}{} + wg.Wait() +} + +func BenchmarkConcurrentDBExec(b *testing.B) { + b.ReportAllocs() + ct := new(concurrentDBExecTest) + for i := 0; i < b.N; i++ { + doConcurrentTest(b, ct) + } +} + +func BenchmarkConcurrentStmtQuery(b *testing.B) { + b.ReportAllocs() + ct := new(concurrentStmtQueryTest) + for i := 0; i < b.N; i++ { + doConcurrentTest(b, ct) + } +} + +func BenchmarkConcurrentStmtExec(b *testing.B) { + b.ReportAllocs() + ct := new(concurrentStmtExecTest) + for i := 0; i < b.N; i++ { + doConcurrentTest(b, ct) + } +} + +func BenchmarkConcurrentTxQuery(b *testing.B) { + b.ReportAllocs() + ct := new(concurrentTxQueryTest) + for i := 0; i < b.N; i++ { + doConcurrentTest(b, ct) + } +} + +func BenchmarkConcurrentTxExec(b *testing.B) { + b.ReportAllocs() + ct := new(concurrentTxExecTest) + for i := 0; i < b.N; i++ { + doConcurrentTest(b, ct) + } +} + +func BenchmarkConcurrentTxStmtQuery(b *testing.B) { + b.ReportAllocs() + ct := new(concurrentTxStmtQueryTest) + for i := 0; i < b.N; i++ { + doConcurrentTest(b, ct) + } +} + +func BenchmarkConcurrentTxStmtExec(b *testing.B) { + b.ReportAllocs() + ct := new(concurrentTxStmtExecTest) + for i := 0; i < b.N; i++ { + doConcurrentTest(b, ct) + } } -func BenchmarkConcurrency(b *testing.B) { +func BenchmarkConcurrentRandom(b *testing.B) { b.ReportAllocs() + ct := new(concurrentRandomTest) for i := 0; i < b.N; i++ { - manyConcurrentQueries(b) + doConcurrentTest(b, ct) } } diff --git a/libgo/go/debug/dwarf/entry.go b/libgo/go/debug/dwarf/entry.go index ada7231..4775283 100644 --- a/libgo/go/debug/dwarf/entry.go +++ b/libgo/go/debug/dwarf/entry.go @@ -10,7 +10,10 @@ package dwarf -import "errors" +import ( + "errors" + "strconv" +) // a single entry's description: a sequence of attributes type abbrev struct { @@ -152,7 +155,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { var val interface{} switch fmt { default: - b.error("unknown entry attr format") + b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16)) // address case formAddr: diff --git a/libgo/go/debug/dwarf/type.go b/libgo/go/debug/dwarf/type.go index 54000fb..1fbae6c 100644 --- a/libgo/go/debug/dwarf/type.go +++ b/libgo/go/debug/dwarf/type.go @@ -271,24 +271,43 @@ func (d *Data) Type(off Offset) (Type, error) { // d.Type recursively, to handle circular types correctly. var typ Type + nextDepth := 0 + // Get next child; set err if error happens. next := func() *Entry { if !e.Children { return nil } - kid, err1 := r.Next() - if err1 != nil { - err = err1 - return nil - } - if kid == nil { - err = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"} - return nil - } - if kid.Tag == 0 { - return nil + // Only return direct children. + // Skip over composite entries that happen to be nested + // inside this one. Most DWARF generators wouldn't generate + // such a thing, but clang does. + // See golang.org/issue/6472. + for { + kid, err1 := r.Next() + if err1 != nil { + err = err1 + return nil + } + if kid == nil { + err = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"} + return nil + } + if kid.Tag == 0 { + if nextDepth > 0 { + nextDepth-- + continue + } + return nil + } + if kid.Children { + nextDepth++ + } + if nextDepth > 0 { + continue + } + return kid } - return kid } // Get Type referred to by Entry's AttrType field. diff --git a/libgo/go/debug/elf/file_test.go b/libgo/go/debug/elf/file_test.go index f9aa726..38b5f9e 100644 --- a/libgo/go/debug/elf/file_test.go +++ b/libgo/go/debug/elf/file_test.go @@ -166,6 +166,7 @@ func TestOpen(t *testing.T) { } else { f, err = Open(tt.file) } + defer f.Close() if err != nil { t.Errorf("cannot open file %s: %v", tt.file, err) continue diff --git a/libgo/go/debug/gosym/pclntab.go b/libgo/go/debug/gosym/pclntab.go index 9d7b0d1..3e6a8046 100644 --- a/libgo/go/debug/gosym/pclntab.go +++ b/libgo/go/debug/gosym/pclntab.go @@ -8,16 +8,47 @@ package gosym -import "encoding/binary" +import ( + "encoding/binary" + "sync" +) +// A LineTable is a data structure mapping program counters to line numbers. +// +// In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable, +// and the line number corresponded to a numbering of all source lines in the +// program, across all files. That absolute line number would then have to be +// converted separately to a file name and line number within the file. +// +// In Go 1.2, the format of the data changed so that there is a single LineTable +// for the entire program, shared by all Funcs, and there are no absolute line +// numbers, just line numbers within specific files. +// +// For the most part, LineTable's methods should be treated as an internal +// detail of the package; callers should use the methods on Table instead. type LineTable struct { Data []byte PC uint64 Line int + + // Go 1.2 state + mu sync.Mutex + go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes + binary binary.ByteOrder + quantum uint32 + ptrsize uint32 + functab []byte + nfunctab uint32 + filetab []byte + nfiletab uint32 + fileMap map[string]uint32 } -// TODO(rsc): Need to pull in quantum from architecture definition. -const quantum = 1 +// NOTE(rsc): This is wrong for GOARCH=arm, which uses a quantum of 4, +// but we have no idea whether we're using arm or not. This only +// matters in the old (pre-Go 1.2) symbol table format, so it's not worth +// fixing. +const oldQuantum = 1 func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, line int) { // The PC/line table can be thought of as a sequence of @@ -46,31 +77,42 @@ func (t *LineTable) parse(targetPC uint64, targetLine int) (b []byte, pc uint64, case code <= 128: line -= int(code - 64) default: - pc += quantum * uint64(code-128) + pc += oldQuantum * uint64(code-128) continue } - pc += quantum + pc += oldQuantum } return b, pc, line } func (t *LineTable) slice(pc uint64) *LineTable { data, pc, line := t.parse(pc, -1) - return &LineTable{data, pc, line} + return &LineTable{Data: data, PC: pc, Line: line} } +// PCToLine returns the line number for the given program counter. +// Callers should use Table's PCToLine method instead. func (t *LineTable) PCToLine(pc uint64) int { + if t.isGo12() { + return t.go12PCToLine(pc) + } _, _, line := t.parse(pc, -1) return line } +// LineToPC returns the program counter for the given line number, +// considering only program counters before maxpc. +// Callers should use Table's LineToPC method instead. func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 { + if t.isGo12() { + return 0 + } _, pc, line1 := t.parse(maxpc, line) if line1 != line { return 0 } // Subtract quantum from PC to account for post-line increment - return pc - quantum + return pc - oldQuantum } // NewLineTable returns a new PC/line table @@ -78,5 +120,307 @@ func (t *LineTable) LineToPC(line int, maxpc uint64) uint64 { // Text must be the start address of the // corresponding text segment. func NewLineTable(data []byte, text uint64) *LineTable { - return &LineTable{data, text, 0} + return &LineTable{Data: data, PC: text, Line: 0} +} + +// Go 1.2 symbol table format. +// See golang.org/s/go12symtab. +// +// A general note about the methods here: rather than try to avoid +// index out of bounds errors, we trust Go to detect them, and then +// we recover from the panics and treat them as indicative of a malformed +// or incomplete table. +// +// The methods called by symtab.go, which begin with "go12" prefixes, +// are expected to have that recovery logic. + +// isGo12 reports whether this is a Go 1.2 (or later) symbol table. +func (t *LineTable) isGo12() bool { + t.go12Init() + return t.go12 == 1 +} + +const go12magic = 0xfffffffb + +// uintptr returns the pointer-sized value encoded at b. +// The pointer size is dictated by the table being read. +func (t *LineTable) uintptr(b []byte) uint64 { + if t.ptrsize == 4 { + return uint64(t.binary.Uint32(b)) + } + return t.binary.Uint64(b) +} + +// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table. +func (t *LineTable) go12Init() { + t.mu.Lock() + defer t.mu.Unlock() + if t.go12 != 0 { + return + } + + defer func() { + // If we panic parsing, assume it's not a Go 1.2 symbol table. + recover() + }() + + // Check header: 4-byte magic, two zeros, pc quantum, pointer size. + t.go12 = -1 // not Go 1.2 until proven otherwise + if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 || + (t.Data[6] != 1 && t.Data[6] != 4) || // pc quantum + (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size + return + } + + switch uint32(go12magic) { + case binary.LittleEndian.Uint32(t.Data): + t.binary = binary.LittleEndian + case binary.BigEndian.Uint32(t.Data): + t.binary = binary.BigEndian + default: + return + } + + t.quantum = uint32(t.Data[6]) + t.ptrsize = uint32(t.Data[7]) + + t.nfunctab = uint32(t.uintptr(t.Data[8:])) + t.functab = t.Data[8+t.ptrsize:] + functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize + fileoff := t.binary.Uint32(t.functab[functabsize:]) + t.functab = t.functab[:functabsize] + t.filetab = t.Data[fileoff:] + t.nfiletab = t.binary.Uint32(t.filetab) + t.filetab = t.filetab[:t.nfiletab*4] + + t.go12 = 1 // so far so good +} + +// findFunc returns the func corresponding to the given program counter. +func (t *LineTable) findFunc(pc uint64) []byte { + if pc < t.uintptr(t.functab) || pc >= t.uintptr(t.functab[len(t.functab)-int(t.ptrsize):]) { + return nil + } + + // The function table is a list of 2*nfunctab+1 uintptrs, + // alternating program counters and offsets to func structures. + f := t.functab + nf := t.nfunctab + for nf > 0 { + m := nf / 2 + fm := f[2*t.ptrsize*m:] + if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) { + return t.Data[t.uintptr(fm[t.ptrsize:]):] + } else if pc < t.uintptr(fm) { + nf = m + } else { + f = f[(m+1)*2*t.ptrsize:] + nf -= m + 1 + } + } + return nil +} + +// readvarint reads, removes, and returns a varint from *pp. +func (t *LineTable) readvarint(pp *[]byte) uint32 { + var v, shift uint32 + p := *pp + for shift = 0; ; shift += 7 { + b := p[0] + p = p[1:] + v |= (uint32(b) & 0x7F) << shift + if b&0x80 == 0 { + break + } + } + *pp = p + return v +} + +// string returns a Go string found at off. +func (t *LineTable) string(off uint32) string { + for i := off; ; i++ { + if t.Data[i] == 0 { + return string(t.Data[off:i]) + } + } +} + +// step advances to the next pc, value pair in the encoded table. +func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { + uvdelta := t.readvarint(p) + if uvdelta == 0 && !first { + return false + } + if uvdelta&1 != 0 { + uvdelta = ^(uvdelta >> 1) + } else { + uvdelta >>= 1 + } + vdelta := int32(uvdelta) + pcdelta := t.readvarint(p) * t.quantum + *pc += uint64(pcdelta) + *val += vdelta + return true +} + +// pcvalue reports the value associated with the target pc. +// off is the offset to the beginning of the pc-value table, +// and entry is the start PC for the corresponding function. +func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 { + if off == 0 { + return -1 + } + p := t.Data[off:] + + val := int32(-1) + pc := entry + for t.step(&p, &pc, &val, pc == entry) { + if targetpc < pc { + return val + } + } + return -1 +} + +// findFileLine scans one function in the binary looking for a +// program counter in the given file on the given line. +// It does so by running the pc-value tables mapping program counter +// to file number. Since most functions come from a single file, these +// are usually short and quick to scan. If a file match is found, then the +// code goes to the expense of looking for a simultaneous line number match. +func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum, line int32) uint64 { + if filetab == 0 || linetab == 0 { + return 0 + } + + fp := t.Data[filetab:] + fl := t.Data[linetab:] + fileVal := int32(-1) + filePC := entry + lineVal := int32(-1) + linePC := entry + fileStartPC := filePC + for t.step(&fp, &filePC, &fileVal, filePC == entry) { + if fileVal == filenum && fileStartPC < filePC { + // fileVal is in effect starting at fileStartPC up to + // but not including filePC, and it's the file we want. + // Run the PC table looking for a matching line number + // or until we reach filePC. + lineStartPC := linePC + for linePC < filePC && t.step(&fl, &linePC, &lineVal, linePC == entry) { + // lineVal is in effect until linePC, and lineStartPC < filePC. + if lineVal == line { + if fileStartPC <= lineStartPC { + return lineStartPC + } + if fileStartPC < linePC { + return fileStartPC + } + } + lineStartPC = linePC + } + } + fileStartPC = filePC + } + return 0 +} + +// go12PCToLine maps program counter to line number for the Go 1.2 pcln table. +func (t *LineTable) go12PCToLine(pc uint64) (line int) { + defer func() { + if recover() != nil { + line = -1 + } + }() + + f := t.findFunc(pc) + if f == nil { + return -1 + } + entry := t.uintptr(f) + linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + return int(t.pcvalue(linetab, entry, pc)) +} + +// go12PCToFile maps program counter to file name for the Go 1.2 pcln table. +func (t *LineTable) go12PCToFile(pc uint64) (file string) { + defer func() { + if recover() != nil { + file = "" + } + }() + + f := t.findFunc(pc) + if f == nil { + return "" + } + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + fno := t.pcvalue(filetab, entry, pc) + if fno <= 0 { + return "" + } + return t.string(t.binary.Uint32(t.filetab[4*fno:])) +} + +// go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. +func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) { + defer func() { + if recover() != nil { + pc = 0 + } + }() + + t.initFileMap() + filenum := t.fileMap[file] + if filenum == 0 { + return 0 + } + + // Scan all functions. + // If this turns out to be a bottleneck, we could build a map[int32][]int32 + // mapping file number to a list of functions with code from that file. + for i := uint32(0); i < t.nfunctab; i++ { + f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):] + entry := t.uintptr(f) + filetab := t.binary.Uint32(f[t.ptrsize+4*4:]) + linetab := t.binary.Uint32(f[t.ptrsize+5*4:]) + pc := t.findFileLine(entry, filetab, linetab, int32(filenum), int32(line)) + if pc != 0 { + return pc + } + } + return 0 +} + +// initFileMap initializes the map from file name to file number. +func (t *LineTable) initFileMap() { + t.mu.Lock() + defer t.mu.Unlock() + + if t.fileMap != nil { + return + } + m := make(map[string]uint32) + + for i := uint32(1); i < t.nfiletab; i++ { + s := t.string(t.binary.Uint32(t.filetab[4*i:])) + m[s] = i + } + t.fileMap = m +} + +// go12MapFiles adds to m a key for every file in the Go 1.2 LineTable. +// Every key maps to obj. That's not a very interesting map, but it provides +// a way for callers to obtain the list of files in the program. +func (t *LineTable) go12MapFiles(m map[string]*Obj, obj *Obj) { + defer func() { + recover() + }() + + t.initFileMap() + for file := range t.fileMap { + m[file] = obj + } } diff --git a/libgo/go/debug/gosym/pclntab_test.go b/libgo/go/debug/gosym/pclntab_test.go index 20acba6..35502e8 100644 --- a/libgo/go/debug/gosym/pclntab_test.go +++ b/libgo/go/debug/gosym/pclntab_test.go @@ -21,9 +21,17 @@ var ( pclinetestBinary string ) -func dotest() bool { - // For now, only works on ELF platforms. - if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { +func dotest(self bool) bool { + // For now, only works on amd64 platforms. + if runtime.GOARCH != "amd64" { + return false + } + // Self test reads test binary; only works on Linux. + if self && runtime.GOOS != "linux" { + return false + } + // Command below expects "sh", so Unix. + if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return false } if pclinetestBinary != "" { @@ -41,7 +49,7 @@ func dotest() bool { // the resulting binary looks like it was built from pclinetest.s, // but we have renamed it to keep it away from the go tool. pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest") - command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6", + command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6", pclinetestBinary, pclinetestBinary, pclinetestBinary) cmd := exec.Command("sh", "-c", command) cmd.Stdout = os.Stdout @@ -100,12 +108,16 @@ func parse(file string, f *elf.File, t *testing.T) (*elf.File, *Table) { var goarch = os.Getenv("O") func TestLineFromAline(t *testing.T) { - if !dotest() { + if !dotest(true) { return } defer endtest() tab := getTable(t) + if tab.go12line != nil { + // aline's don't exist in the Go 1.2 table. + t.Skip("not relevant to Go 1.2 symbol table") + } // Find the sym package pkg := tab.LookupFunc("debug/gosym.TestLineFromAline").Obj @@ -148,12 +160,16 @@ func TestLineFromAline(t *testing.T) { } func TestLineAline(t *testing.T) { - if !dotest() { + if !dotest(true) { return } defer endtest() tab := getTable(t) + if tab.go12line != nil { + // aline's don't exist in the Go 1.2 table. + t.Skip("not relevant to Go 1.2 symbol table") + } for _, o := range tab.Files { // A source file can appear multiple times in a @@ -190,7 +206,7 @@ func TestLineAline(t *testing.T) { } func TestPCLine(t *testing.T) { - if !dotest() { + if !dotest(false) { return } defer endtest() @@ -206,16 +222,17 @@ func TestPCLine(t *testing.T) { sym := tab.LookupFunc("linefrompc") wantLine := 0 for pc := sym.Entry; pc < sym.End; pc++ { - file, line, fn := tab.PCToLine(pc) off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g + if textdat[off] == 255 { + break + } wantLine += int(textdat[off]) - t.Logf("off is %d", off) + t.Logf("off is %d %#x (max %d)", off, textdat[off], sym.End-pc) + file, line, fn := tab.PCToLine(pc) if fn == nil { t.Errorf("failed to get line of PC %#x", pc) - } else if !strings.HasSuffix(file, "pclinetest.asm") { - t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name) - } else if line != wantLine || fn != sym { - t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name) + } else if !strings.HasSuffix(file, "pclinetest.asm") || line != wantLine || fn != sym { + t.Errorf("PCToLine(%#x) = %s:%d (%s), want %s:%d (%s)", pc, file, line, fn.Name, "pclinetest.asm", wantLine, sym.Name) } } @@ -227,6 +244,9 @@ func TestPCLine(t *testing.T) { for pc := sym.Value; pc < sym.End; pc += 2 + uint64(textdat[off]) { file, line, fn := tab.PCToLine(pc) off = pc - text.Addr + if textdat[off] == 255 { + break + } wantLine += int(textdat[off]) if line != wantLine { t.Errorf("expected line %d at PC %#x in pcfromline, got %d", wantLine, pc, line) diff --git a/libgo/go/debug/gosym/symtab.go b/libgo/go/debug/gosym/symtab.go index 81ed4fb..9ab05ba 100644 --- a/libgo/go/debug/gosym/symtab.go +++ b/libgo/go/debug/gosym/symtab.go @@ -34,7 +34,7 @@ type Sym struct { Func *Func } -// Static returns whether this symbol is static (not visible outside its file). +// Static reports whether this symbol is static (not visible outside its file). func (s *Sym) Static() bool { return s.Type >= 'a' } // PackageName returns the package part of the symbol name, @@ -77,10 +77,26 @@ type Func struct { Obj *Obj } -// An Obj represents a single object file. +// An Obj represents a collection of functions in a symbol table. +// +// The exact method of division of a binary into separate Objs is an internal detail +// of the symbol table format. +// +// In early versions of Go each source file became a different Obj. +// +// In Go 1 and Go 1.1, each package produced one Obj for all Go sources +// and one Obj per C source file. +// +// In Go 1.2, there is a single Obj for the entire program. type Obj struct { + // Funcs is a list of functions in the Obj. Funcs []Func - Paths []Sym + + // In Go 1.1 and earlier, Paths is a list of symbols corresponding + // to the source file names that produced the Obj. + // In Go 1.2, Paths is nil. + // Use the keys of Table.Files to obtain a list of source files. + Paths []Sym // meta } /* @@ -93,9 +109,10 @@ type Obj struct { type Table struct { Syms []Sym Funcs []Func - Files map[string]*Obj - Objs []Obj - // textEnd uint64; + Files map[string]*Obj // nil for Go 1.2 and later binaries + Objs []Obj // nil for Go 1.2 and later binaries + + go12line *LineTable // Go 1.2 line number table } type sym struct { @@ -105,10 +122,11 @@ type sym struct { name []byte } -var littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} -var bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} - -var oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} +var ( + littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} + bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} +) func walksymtab(data []byte, fn func(sym) error) error { var order binary.ByteOrder = binary.BigEndian @@ -260,6 +278,9 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { } var t Table + if pcln.isGo12() { + t.go12line = pcln + } fname := make(map[uint16]string) t.Syms = make([]Sym, 0, n) nf := 0 @@ -316,17 +337,29 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { } t.Funcs = make([]Func, 0, nf) - t.Objs = make([]Obj, 0, nz) t.Files = make(map[string]*Obj) + var obj *Obj + if t.go12line != nil { + // Put all functions into one Obj. + t.Objs = make([]Obj, 1) + obj = &t.Objs[0] + t.go12line.go12MapFiles(t.Files, obj) + } else { + t.Objs = make([]Obj, 0, nz) + } + // Count text symbols and attach frame sizes, parameters, and // locals to them. Also, find object file boundaries. - var obj *Obj lastf := 0 for i := 0; i < len(t.Syms); i++ { sym := &t.Syms[i] switch sym.Type { case 'Z', 'z': // path symbol + if t.go12line != nil { + // Go 1.2 binaries have the file information elsewhere. Ignore. + break + } // Finish the current object if obj != nil { obj.Funcs = t.Funcs[lastf:] @@ -395,7 +428,12 @@ func NewTable(symtab []byte, pcln *LineTable) (*Table, error) { fn.Sym = sym fn.Entry = sym.Value fn.Obj = obj - if pcln != nil { + if t.go12line != nil { + // All functions share the same line table. + // It knows how to narrow down to a specific + // function quickly. + fn.LineTable = t.go12line + } else if pcln != nil { fn.LineTable = pcln.slice(fn.Entry) pcln = fn.LineTable } @@ -448,18 +486,32 @@ func (t *Table) PCToLine(pc uint64) (file string, line int, fn *Func) { if fn = t.PCToFunc(pc); fn == nil { return } - file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc)) + if t.go12line != nil { + file = t.go12line.go12PCToFile(pc) + line = t.go12line.go12PCToLine(pc) + } else { + file, line = fn.Obj.lineFromAline(fn.LineTable.PCToLine(pc)) + } return } // LineToPC looks up the first program counter on the given line in -// the named file. Returns UnknownPathError or UnknownLineError if +// the named file. It returns UnknownPathError or UnknownLineError if // there is an error looking up this line. func (t *Table) LineToPC(file string, line int) (pc uint64, fn *Func, err error) { obj, ok := t.Files[file] if !ok { return 0, nil, UnknownFileError(file) } + + if t.go12line != nil { + pc := t.go12line.go12LineToPC(file, line) + if pc == 0 { + return 0, nil, &UnknownLineError{file, line} + } + return pc, t.PCToFunc(pc), nil + } + abs, err := obj.alineFromLine(file, line) if err != nil { return @@ -503,9 +555,7 @@ func (t *Table) LookupFunc(name string) *Func { } // SymByAddr returns the text, data, or bss symbol starting at the given address. -// TODO(rsc): Allow lookup by any address within the symbol. func (t *Table) SymByAddr(addr uint64) *Sym { - // TODO(austin) Maybe make a map for i := range t.Syms { s := &t.Syms[i] switch s.Type { @@ -522,6 +572,13 @@ func (t *Table) SymByAddr(addr uint64) *Sym { * Object files */ +// This is legacy code for Go 1.1 and earlier, which used the +// Plan 9 format for pc-line tables. This code was never quite +// correct. It's probably very close, and it's usually correct, but +// we never quite found all the corner cases. +// +// Go 1.2 and later use a simpler format, documented at golang.org/s/go12symtab. + func (o *Obj) lineFromAline(aline int) (string, int) { type stackEnt struct { path string @@ -533,8 +590,6 @@ func (o *Obj) lineFromAline(aline int) (string, int) { noPath := &stackEnt{"", 0, 0, nil} tos := noPath - // TODO(austin) I have no idea how 'Z' symbols work, except - // that they pop the stack. pathloop: for _, s := range o.Paths { val := int(s.Value) diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go index cac9d64..992356c 100644 --- a/libgo/go/encoding/asn1/asn1.go +++ b/libgo/go/encoding/asn1/asn1.go @@ -32,14 +32,14 @@ type StructuralError struct { Msg string } -func (e StructuralError) Error() string { return "ASN.1 structure error: " + e.Msg } +func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg } // A SyntaxError suggests that the ASN.1 data is invalid. type SyntaxError struct { Msg string } -func (e SyntaxError) Error() string { return "ASN.1 syntax error: " + e.Msg } +func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg } // We start by dealing with each of the primitive types in turn. @@ -51,7 +51,19 @@ func parseBool(bytes []byte) (ret bool, err error) { return } - return bytes[0] != 0, nil + // DER demands that "If the encoding represents the boolean value TRUE, + // its single contents octet shall have all eight bits set to one." + // Thus only 0 and 255 are valid encoded values. + switch bytes[0] { + case 0: + ret = false + case 0xff: + ret = true + default: + err = SyntaxError{"invalid boolean"} + } + + return } // INTEGER @@ -171,7 +183,7 @@ func parseBitString(bytes []byte) (ret BitString, err error) { // An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER. type ObjectIdentifier []int -// Equal returns true iff oi and other represent the same identifier. +// Equal reports whether oi and other represent the same identifier. func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool { if len(oi) != len(other) { return false @@ -198,12 +210,24 @@ func parseObjectIdentifier(bytes []byte) (s []int, err error) { // encoded differently) and then every varint is a single byte long. s = make([]int, len(bytes)+1) - // The first byte is 40*value1 + value2: - s[0] = int(bytes[0]) / 40 - s[1] = int(bytes[0]) % 40 + // The first varint is 40*value1 + value2: + // According to this packing, value1 can take the values 0, 1 and 2 only. + // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, + // then there are no restrictions on value2. + v, offset, err := parseBase128Int(bytes, 0) + if err != nil { + return + } + if v < 80 { + s[0] = v / 40 + s[1] = v % 40 + } else { + s[0] = 2 + s[1] = v - 80 + } + i := 2 - for offset := 1; offset < len(bytes); i++ { - var v int + for ; offset < len(bytes); i++ { v, offset, err = parseBase128Int(bytes, offset) if err != nil { return @@ -573,7 +597,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam } } else { if fieldType != flagType { - err = StructuralError{"Zero length explicit tag was not an asn1.Flag"} + err = StructuralError{"zero length explicit tag was not an asn1.Flag"} return } v.SetBool(true) diff --git a/libgo/go/encoding/asn1/asn1_test.go b/libgo/go/encoding/asn1/asn1_test.go index 6e98dcf..f68804e 100644 --- a/libgo/go/encoding/asn1/asn1_test.go +++ b/libgo/go/encoding/asn1/asn1_test.go @@ -12,6 +12,32 @@ import ( "time" ) +type boolTest struct { + in []byte + ok bool + out bool +} + +var boolTestData = []boolTest{ + {[]byte{0x00}, true, false}, + {[]byte{0xff}, true, true}, + {[]byte{0x00, 0x00}, false, false}, + {[]byte{0xff, 0xff}, false, false}, + {[]byte{0x01}, false, false}, +} + +func TestParseBool(t *testing.T) { + for i, test := range boolTestData { + ret, err := parseBool(test.in) + if (err == nil) != test.ok { + t.Errorf("#%d: Incorrect error result (did fail? %v, expected: %v)", i, err == nil, test.ok) + } + if test.ok && ret != test.out { + t.Errorf("#%d: Bad result: %v (expected %v)", i, ret, test.out) + } + } +} + type int64Test struct { in []byte ok bool @@ -183,6 +209,7 @@ var objectIdentifierTestData = []objectIdentifierTest{ {[]byte{85}, true, []int{2, 5}}, {[]byte{85, 0x02}, true, []int{2, 5, 2}}, {[]byte{85, 0x02, 0xc0, 0x00}, true, []int{2, 5, 2, 0x2000}}, + {[]byte{0x81, 0x34, 0x03}, true, []int{2, 100, 3}}, {[]byte{85, 0x02, 0xc0, 0x80, 0x80, 0x80, 0x80}, false, []int{}}, } @@ -378,7 +405,7 @@ var unmarshalTestData = []struct { {[]byte{0x30, 0x03, 0x81, 0x01, 0x01}, &TestContextSpecificTags{1}}, {[]byte{0x30, 0x08, 0xa1, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x02}, &TestContextSpecificTags2{1, 2}}, {[]byte{0x01, 0x01, 0x00}, newBool(false)}, - {[]byte{0x01, 0x01, 0x01}, newBool(true)}, + {[]byte{0x01, 0x01, 0xff}, newBool(true)}, {[]byte{0x30, 0x0b, 0x13, 0x03, 0x66, 0x6f, 0x6f, 0x02, 0x01, 0x22, 0x02, 0x01, 0x33}, &TestElementsAfterString{"foo", 0x22, 0x33}}, {[]byte{0x30, 0x05, 0x02, 0x03, 0x12, 0x34, 0x56}, &TestBigInt{big.NewInt(0x123456)}}, } diff --git a/libgo/go/encoding/asn1/marshal.go b/libgo/go/encoding/asn1/marshal.go index adaf80d..ed17e41 100644 --- a/libgo/go/encoding/asn1/marshal.go +++ b/libgo/go/encoding/asn1/marshal.go @@ -240,11 +240,11 @@ func marshalBitString(out *forkableWriter, b BitString) (err error) { } func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) { - if len(oid) < 2 || oid[0] > 6 || oid[1] >= 40 { + if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) { return StructuralError{"invalid object identifier"} } - err = out.WriteByte(byte(oid[0]*40 + oid[1])) + err = marshalBase128Int(out, int64(oid[0]*40+oid[1])) if err != nil { return } @@ -304,7 +304,7 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { case 2000 <= year && year < 2050: err = marshalTwoDigits(out, int(year-2000)) default: - return StructuralError{"Cannot represent time as UTCTime"} + return StructuralError{"cannot represent time as UTCTime"} } if err != nil { return @@ -441,11 +441,11 @@ func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameter return } - var params fieldParameters + var fp fieldParameters for i := 0; i < v.Len(); i++ { var pre *forkableWriter pre, out = out.fork() - err = marshalField(pre, v.Index(i), params) + err = marshalField(pre, v.Index(i), fp) if err != nil { return } @@ -501,7 +501,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) class := classUniversal if params.stringType != 0 && tag != tagPrintableString { - return StructuralError{"Explicit string type given to non-string member"} + return StructuralError{"explicit string type given to non-string member"} } if tag == tagPrintableString { @@ -525,7 +525,7 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) if params.set { if tag != tagSequence { - return StructuralError{"Non sequence tagged as set"} + return StructuralError{"non sequence tagged as set"} } tag = tagSet } diff --git a/libgo/go/encoding/asn1/marshal_test.go b/libgo/go/encoding/asn1/marshal_test.go index b4dbe71..763c86d 100644 --- a/libgo/go/encoding/asn1/marshal_test.go +++ b/libgo/go/encoding/asn1/marshal_test.go @@ -87,6 +87,7 @@ var marshalTests = []marshalTest{ {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, {ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"}, + {ObjectIdentifier([]int{2, 100, 3}), "0603813403"}, {"test", "130474657374"}, { "" + diff --git a/libgo/go/encoding/binary/binary.go b/libgo/go/encoding/binary/binary.go index edbac19..f3466b9 100644 --- a/libgo/go/encoding/binary/binary.go +++ b/libgo/go/encoding/binary/binary.go @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package binary implements translation between numbers and byte sequences -// and encoding and decoding of varints. +// Package binary implements simple translation between numbers and byte +// sequences and encoding and decoding of varints. // // Numbers are translated by reading and writing fixed-size values. // A fixed-size value is either a fixed-size arithmetic @@ -13,6 +13,11 @@ // Varints are a method of encoding integers using one or more bytes; // numbers with smaller absolute value take a smaller number of bytes. // For a specification, see http://code.google.com/apis/protocolbuffers/docs/encoding.html. +// +// This package favors simplicity over efficiency. Clients that require +// high-performance serialization, especially for large data structures, +// should look at more advanced solutions such as the encoding/gob +// package or protocol buffers. package binary import ( @@ -129,30 +134,65 @@ func (bigEndian) GoString() string { return "binary.BigEndian" } // blank (_) field names is skipped; i.e., blank field names // may be used for padding. func Read(r io.Reader, order ByteOrder, data interface{}) error { - // Fast path for basic types. - if n := intDestSize(data); n != 0 { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { var b [8]byte - bs := b[:n] + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } if _, err := io.ReadFull(r, bs); err != nil { return err } - switch v := data.(type) { + switch data := data.(type) { case *int8: - *v = int8(b[0]) + *data = int8(b[0]) case *uint8: - *v = b[0] + *data = b[0] case *int16: - *v = int16(order.Uint16(bs)) + *data = int16(order.Uint16(bs)) case *uint16: - *v = order.Uint16(bs) + *data = order.Uint16(bs) case *int32: - *v = int32(order.Uint32(bs)) + *data = int32(order.Uint32(bs)) case *uint32: - *v = order.Uint32(bs) + *data = order.Uint32(bs) case *int64: - *v = int64(order.Uint64(bs)) + *data = int64(order.Uint64(bs)) case *uint64: - *v = order.Uint64(bs) + *data = order.Uint64(bs) + case []int8: + for i, x := range bs { // Easier to loop over the input for 8-bit values. + data[i] = int8(x) + } + case []uint8: + copy(data, bs) + case []int16: + for i := range data { + data[i] = int16(order.Uint16(bs[2*i:])) + } + case []uint16: + for i := range data { + data[i] = order.Uint16(bs[2*i:]) + } + case []int32: + for i := range data { + data[i] = int32(order.Uint32(bs[4*i:])) + } + case []uint32: + for i := range data { + data[i] = order.Uint32(bs[4*i:]) + } + case []int64: + for i := range data { + data[i] = int64(order.Uint64(bs[8*i:])) + } + case []uint64: + for i := range data { + data[i] = order.Uint64(bs[8*i:]) + } } return nil } @@ -187,60 +227,95 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error { // When writing structs, zero values are written for fields // with blank (_) field names. func Write(w io.Writer, order ByteOrder, data interface{}) error { - // Fast path for basic types. - var b [8]byte - var bs []byte - switch v := data.(type) { - case *int8: - bs = b[:1] - b[0] = byte(*v) - case int8: - bs = b[:1] - b[0] = byte(v) - case *uint8: - bs = b[:1] - b[0] = *v - case uint8: - bs = b[:1] - b[0] = byte(v) - case *int16: - bs = b[:2] - order.PutUint16(bs, uint16(*v)) - case int16: - bs = b[:2] - order.PutUint16(bs, uint16(v)) - case *uint16: - bs = b[:2] - order.PutUint16(bs, *v) - case uint16: - bs = b[:2] - order.PutUint16(bs, v) - case *int32: - bs = b[:4] - order.PutUint32(bs, uint32(*v)) - case int32: - bs = b[:4] - order.PutUint32(bs, uint32(v)) - case *uint32: - bs = b[:4] - order.PutUint32(bs, *v) - case uint32: - bs = b[:4] - order.PutUint32(bs, v) - case *int64: - bs = b[:8] - order.PutUint64(bs, uint64(*v)) - case int64: - bs = b[:8] - order.PutUint64(bs, uint64(v)) - case *uint64: - bs = b[:8] - order.PutUint64(bs, *v) - case uint64: - bs = b[:8] - order.PutUint64(bs, v) - } - if bs != nil { + // Fast path for basic types and slices. + if n := intDataSize(data); n != 0 { + var b [8]byte + var bs []byte + if n > len(b) { + bs = make([]byte, n) + } else { + bs = b[:n] + } + switch v := data.(type) { + case *int8: + bs = b[:1] + b[0] = byte(*v) + case int8: + bs = b[:1] + b[0] = byte(v) + case []int8: + for i, x := range v { + bs[i] = byte(x) + } + case *uint8: + bs = b[:1] + b[0] = *v + case uint8: + bs = b[:1] + b[0] = byte(v) + case []uint8: + bs = v + case *int16: + bs = b[:2] + order.PutUint16(bs, uint16(*v)) + case int16: + bs = b[:2] + order.PutUint16(bs, uint16(v)) + case []int16: + for i, x := range v { + order.PutUint16(bs[2*i:], uint16(x)) + } + case *uint16: + bs = b[:2] + order.PutUint16(bs, *v) + case uint16: + bs = b[:2] + order.PutUint16(bs, v) + case []uint16: + for i, x := range v { + order.PutUint16(bs[2*i:], x) + } + case *int32: + bs = b[:4] + order.PutUint32(bs, uint32(*v)) + case int32: + bs = b[:4] + order.PutUint32(bs, uint32(v)) + case []int32: + for i, x := range v { + order.PutUint32(bs[4*i:], uint32(x)) + } + case *uint32: + bs = b[:4] + order.PutUint32(bs, *v) + case uint32: + bs = b[:4] + order.PutUint32(bs, v) + case []uint32: + for i, x := range v { + order.PutUint32(bs[4*i:], x) + } + case *int64: + bs = b[:8] + order.PutUint64(bs, uint64(*v)) + case int64: + bs = b[:8] + order.PutUint64(bs, uint64(v)) + case []int64: + for i, x := range v { + order.PutUint64(bs[8*i:], uint64(x)) + } + case *uint64: + bs = b[:8] + order.PutUint64(bs, *v) + case uint64: + bs = b[:8] + order.PutUint64(bs, v) + case []uint64: + for i, x := range v { + order.PutUint64(bs[8*i:], x) + } + } _, err := w.Write(bs) return err } @@ -530,18 +605,34 @@ func (e *encoder) skip(v reflect.Value) { e.buf = e.buf[n:] } -// intDestSize returns the size of the integer that ptrType points to, -// or 0 if the type is not supported. -func intDestSize(ptrType interface{}) int { - switch ptrType.(type) { - case *int8, *uint8: +// intDataSize returns the size of the data required to represent the data when encoded. +// It returns zero if the type cannot be implemented by the fast path in Read or Write. +func intDataSize(data interface{}) int { + switch data := data.(type) { + case int8, *int8, *uint8: return 1 - case *int16, *uint16: + case []int8: + return len(data) + case []uint8: + return len(data) + case int16, *int16, *uint16: return 2 - case *int32, *uint32: + case []int16: + return 2 * len(data) + case []uint16: + return 2 * len(data) + case int32, *int32, *uint32: return 4 - case *int64, *uint64: + case []int32: + return 4 * len(data) + case []uint32: + return 4 * len(data) + case int64, *int64, *uint64: return 8 + case []int64: + return 8 * len(data) + case []uint64: + return 8 * len(data) } return 0 } diff --git a/libgo/go/encoding/binary/binary_test.go b/libgo/go/encoding/binary/binary_test.go index 056f099..fdfee7d 100644 --- a/libgo/go/encoding/binary/binary_test.go +++ b/libgo/go/encoding/binary/binary_test.go @@ -141,6 +141,52 @@ func TestWriteSlice(t *testing.T) { checkResult(t, "WriteSlice", BigEndian, err, buf.Bytes(), src) } +// Addresses of arrays are easier to manipulate with reflection than are slices. +var intArrays = []interface{}{ + &[100]int8{}, + &[100]int16{}, + &[100]int32{}, + &[100]int64{}, + &[100]uint8{}, + &[100]uint16{}, + &[100]uint32{}, + &[100]uint64{}, +} + +func TestSliceRoundTrip(t *testing.T) { + buf := new(bytes.Buffer) + for _, array := range intArrays { + src := reflect.ValueOf(array).Elem() + unsigned := false + switch src.Index(0).Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + unsigned = true + } + for i := 0; i < src.Len(); i++ { + if unsigned { + src.Index(i).SetUint(uint64(i * 0x07654321)) + } else { + src.Index(i).SetInt(int64(i * 0x07654321)) + } + } + buf.Reset() + srcSlice := src.Slice(0, src.Len()) + err := Write(buf, BigEndian, srcSlice.Interface()) + if err != nil { + t.Fatal(err) + } + dst := reflect.New(src.Type()).Elem() + dstSlice := dst.Slice(0, dst.Len()) + err = Read(buf, BigEndian, dstSlice.Interface()) + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(src.Interface(), dst.Interface()) { + t.Fatal(src) + } + } +} + func TestWriteT(t *testing.T) { buf := new(bytes.Buffer) ts := T{} @@ -312,3 +358,16 @@ func BenchmarkWriteInts(b *testing.B) { b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30]) } } + +func BenchmarkWriteSlice1000Int32s(b *testing.B) { + slice := make([]int32, 1000) + buf := new(bytes.Buffer) + var w io.Writer = buf + b.SetBytes(4 * 1000) + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + Write(w, BigEndian, slice) + } + b.StopTimer() +} diff --git a/libgo/go/encoding/csv/reader.go b/libgo/go/encoding/csv/reader.go index b099caf..b328dcc 100644 --- a/libgo/go/encoding/csv/reader.go +++ b/libgo/go/encoding/csv/reader.go @@ -72,7 +72,7 @@ func (e *ParseError) Error() string { // These are the errors that can be returned in ParseError.Error var ( - ErrTrailingComma = errors.New("extra delimiter at end of line") + ErrTrailingComma = errors.New("extra delimiter at end of line") // no longer used ErrBareQuote = errors.New("bare \" in non-quoted-field") ErrQuote = errors.New("extraneous \" in field") ErrFieldCount = errors.New("wrong number of fields in line") @@ -98,16 +98,14 @@ var ( // If LazyQuotes is true, a quote may appear in an unquoted field and a // non-doubled quote may appear in a quoted field. // -// If TrailingComma is true, the last field may be an unquoted empty field. -// // If TrimLeadingSpace is true, leading white space in a field is ignored. type Reader struct { - Comma rune // Field delimiter (set to ',' by NewReader) - Comment rune // Comment character for start of line - FieldsPerRecord int // Number of expected fields per record - LazyQuotes bool // Allow lazy quotes - TrailingComma bool // Allow trailing comma - TrimLeadingSpace bool // Trim leading space + Comma rune // field delimiter (set to ',' by NewReader) + Comment rune // comment character for start of line + FieldsPerRecord int // number of expected fields per record + LazyQuotes bool // allow lazy quotes + TrailingComma bool // ignored; here for backwards compatibility + TrimLeadingSpace bool // trim leading space line int column int r *bufio.Reader @@ -257,23 +255,15 @@ func (r *Reader) parseField() (haveField bool, delim rune, err error) { r.field.Reset() r1, err := r.readRune() - if err != nil { - // If we have EOF and are not at the start of a line - // then we return the empty field. We have already - // checked for trailing commas if needed. - if err == io.EOF && r.column != 0 { - return true, 0, err - } - return false, 0, err + for err == nil && r.TrimLeadingSpace && r1 != '\n' && unicode.IsSpace(r1) { + r1, err = r.readRune() } - if r.TrimLeadingSpace { - for r1 != '\n' && unicode.IsSpace(r1) { - r1, err = r.readRune() - if err != nil { - return false, 0, err - } - } + if err == io.EOF && r.column != 0 { + return true, 0, err + } + if err != nil { + return false, 0, err } switch r1 { @@ -349,25 +339,5 @@ func (r *Reader) parseField() (haveField bool, delim rune, err error) { return false, 0, err } - if !r.TrailingComma { - // We don't allow trailing commas. See if we - // are at the end of the line (being mindful - // of trimming spaces). - c := r.column - r1, err = r.readRune() - if r.TrimLeadingSpace { - for r1 != '\n' && unicode.IsSpace(r1) { - r1, err = r.readRune() - if err != nil { - break - } - } - } - if err == io.EOF || r1 == '\n' { - r.column = c // report the comma - return false, 0, r.error(ErrTrailingComma) - } - r.unreadRune() - } return true, r1, nil } diff --git a/libgo/go/encoding/csv/reader_test.go b/libgo/go/encoding/csv/reader_test.go index 5fd84a7..123df06 100644 --- a/libgo/go/encoding/csv/reader_test.go +++ b/libgo/go/encoding/csv/reader_test.go @@ -171,32 +171,32 @@ field"`, Output: [][]string{{"a", "b", "c"}, {"d", "e"}}, }, { - Name: "BadTrailingCommaEOF", - Input: "a,b,c,", - Error: "extra delimiter at end of line", Line: 1, Column: 5, + Name: "TrailingCommaEOF", + Input: "a,b,c,", + Output: [][]string{{"a", "b", "c", ""}}, }, { - Name: "BadTrailingCommaEOL", - Input: "a,b,c,\n", - Error: "extra delimiter at end of line", Line: 1, Column: 5, + Name: "TrailingCommaEOL", + Input: "a,b,c,\n", + Output: [][]string{{"a", "b", "c", ""}}, }, { - Name: "BadTrailingCommaSpaceEOF", + Name: "TrailingCommaSpaceEOF", TrimLeadingSpace: true, Input: "a,b,c, ", - Error: "extra delimiter at end of line", Line: 1, Column: 5, + Output: [][]string{{"a", "b", "c", ""}}, }, { - Name: "BadTrailingCommaSpaceEOL", + Name: "TrailingCommaSpaceEOL", TrimLeadingSpace: true, Input: "a,b,c, \n", - Error: "extra delimiter at end of line", Line: 1, Column: 5, + Output: [][]string{{"a", "b", "c", ""}}, }, { - Name: "BadTrailingCommaLine3", + Name: "TrailingCommaLine3", TrimLeadingSpace: true, Input: "a,b,c\nd,e,f\ng,hi,", - Error: "extra delimiter at end of line", Line: 3, Column: 4, + Output: [][]string{{"a", "b", "c"}, {"d", "e", "f"}, {"g", "hi", ""}}, }, { Name: "NotTrailingComma3", @@ -231,7 +231,7 @@ x,,, }, }, { - Name: "Issue 2366", + Name: "TrailingCommaIneffective1", TrailingComma: true, TrimLeadingSpace: true, Input: "a,b,\nc,d,e", @@ -241,11 +241,14 @@ x,,, }, }, { - Name: "Issue 2366a", + Name: "TrailingCommaIneffective2", TrailingComma: false, TrimLeadingSpace: true, Input: "a,b,\nc,d,e", - Error: "extra delimiter at end of line", + Output: [][]string{ + {"a", "b", ""}, + {"c", "d", "e"}, + }, }, } diff --git a/libgo/go/encoding/encoding.go b/libgo/go/encoding/encoding.go new file mode 100644 index 0000000..6d218071 --- /dev/null +++ b/libgo/go/encoding/encoding.go @@ -0,0 +1,48 @@ +// 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 encoding defines interfaces shared by other packages that +// convert data to and from byte-level and textual representations. +// Packages that check for these interfaces include encoding/gob, +// encoding/json, and encoding/xml. As a result, implementing an +// interface once can make a type useful in multiple encodings. +// Standard types that implement these interfaces include time.Time and net.IP. +// The interfaces come in pairs that produce and consume encoded data. +package encoding + +// BinaryMarshaler is the interface implemented by an object that can +// marshal itself into a binary form. +// +// MarshalBinary encodes the receiver into a binary form and returns the result. +type BinaryMarshaler interface { + MarshalBinary() (data []byte, err error) +} + +// BinaryUnmarshaler is the interface implemented by an object that can +// unmarshal a binary representation of itself. +// +// UnmarshalBinary must be able to decode the form generated by MarshalBinary. +// UnmarshalBinary must copy the data if it wishes to retain the data +// after returning. +type BinaryUnmarshaler interface { + UnmarshalBinary(data []byte) error +} + +// TextMarshaler is the interface implemented by an object that can +// marshal itself into a textual form. +// +// MarshalText encodes the receiver into UTF-8-encoded text and returns the result. +type TextMarshaler interface { + MarshalText() (text []byte, err error) +} + +// TextUnmarshaler is the interface implemented by an object that can +// unmarshal a textual representation of itself. +// +// UnmarshalText must be able to decode the form generated by MarshalText. +// UnmarshalText must copy the text if it wishes to retain the text +// after returning. +type TextUnmarshaler interface { + UnmarshalText(text []byte) error +} diff --git a/libgo/go/encoding/gob/codec_test.go b/libgo/go/encoding/gob/codec_test.go index 9e38e31..b40f783 100644 --- a/libgo/go/encoding/gob/codec_test.go +++ b/libgo/go/encoding/gob/codec_test.go @@ -1009,24 +1009,6 @@ func TestBadRecursiveType(t *testing.T) { // Can't test decode easily because we can't encode one, so we can't pass one to a Decoder. } -type Bad0 struct { - CH chan int - C float64 -} - -func TestInvalidField(t *testing.T) { - var bad0 Bad0 - bad0.CH = make(chan int) - b := new(bytes.Buffer) - dummyEncoder := new(Encoder) // sufficient for this purpose. - dummyEncoder.encode(b, reflect.ValueOf(&bad0), userType(reflect.TypeOf(&bad0))) - if err := dummyEncoder.err; err == nil { - t.Error("expected error; got none") - } else if strings.Index(err.Error(), "type") < 0 { - t.Error("expected type error; got", err) - } -} - type Indirect struct { A ***[3]int S ***[]int diff --git a/libgo/go/encoding/gob/debug.go b/libgo/go/encoding/gob/debug.go index 31d1351..6117eb0 100644 --- a/libgo/go/encoding/gob/debug.go +++ b/libgo/go/encoding/gob/debug.go @@ -415,6 +415,16 @@ func (deb *debugger) typeDefinition(indent tab, id typeId) { deb.delta(1) com := deb.common() wire.GobEncoderT = &gobEncoderType{com} + case 5: // BinaryMarshaler type, one field of {{Common}} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + wire.BinaryMarshalerT = &gobEncoderType{com} + case 6: // TextMarshaler type, one field of {{Common}} + // Field number 0 is CommonType + deb.delta(1) + com := deb.common() + wire.TextMarshalerT = &gobEncoderType{com} default: errorf("bad field in type %d", fieldNum) } diff --git a/libgo/go/encoding/gob/decode.go b/libgo/go/encoding/gob/decode.go index 7cc7565..3e76f4c 100644 --- a/libgo/go/encoding/gob/decode.go +++ b/libgo/go/encoding/gob/decode.go @@ -9,6 +9,7 @@ package gob import ( "bytes" + "encoding" "errors" "io" "math" @@ -450,11 +451,11 @@ type decEngine struct { // allocate makes sure storage is available for an object of underlying type rtyp // that is indir levels of indirection through p. -func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { +func allocate(rtyp reflect.Type, p unsafe.Pointer, indir int) unsafe.Pointer { if indir == 0 { return p } - up := unsafe.Pointer(p) + up := p if indir > 1 { up = decIndirect(up, indir) } @@ -462,13 +463,13 @@ func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr { // Allocate object. *(*unsafe.Pointer)(up) = unsafe.Pointer(reflect.New(rtyp).Pointer()) } - return *(*uintptr)(up) + return *(*unsafe.Pointer)(up) } // decodeSingle decodes a top-level value that is not a struct and stores it through p. // Such values are preceded by a zero, making them have the memory layout of a // struct field (although with an illegal field number). -func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) { +func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep unsafe.Pointer) { state := dec.newDecoderState(&dec.buf) state.fieldnum = singletonField delta := int(state.decodeUint()) @@ -479,7 +480,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint if instr.indir != ut.indir { errorf("internal error: inconsistent indirection instr %d ut %d", instr.indir, ut.indir) } - ptr := unsafe.Pointer(basep) // offset will be zero + ptr := basep // offset will be zero if instr.indir > 1 { ptr = decIndirect(ptr, instr.indir) } @@ -492,7 +493,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint // differ from ut.indir, which was computed when the engine was built. // This state cannot arise for decodeSingle, which is called directly // from the user's value, not from the innards of an engine. -func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, indir int) { +func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p unsafe.Pointer, indir int) { p = allocate(ut.base, p, indir) state := dec.newDecoderState(&dec.buf) state.fieldnum = -1 @@ -511,7 +512,7 @@ func (dec *Decoder) decodeStruct(engine *decEngine, ut *userTypeInfo, p uintptr, break } instr := &engine.instr[fieldnum] - p := unsafe.Pointer(basep + instr.offset) + p := unsafe.Pointer(uintptr(basep) + instr.offset) if instr.indir > 1 { p = decIndirect(p, instr.indir) } @@ -559,25 +560,25 @@ func (dec *Decoder) ignoreSingle(engine *decEngine) { } // decodeArrayHelper does the work for decoding arrays and slices. -func (dec *Decoder) decodeArrayHelper(state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) { +func (dec *Decoder) decodeArrayHelper(state *decoderState, p unsafe.Pointer, elemOp decOp, elemWid uintptr, length, elemIndir int, ovfl error) { instr := &decInstr{elemOp, 0, elemIndir, 0, ovfl} for i := 0; i < length; i++ { if state.b.Len() == 0 { errorf("decoding array or slice: length exceeds input size (%d elements)", length) } - up := unsafe.Pointer(p) + up := p if elemIndir > 1 { up = decIndirect(up, elemIndir) } elemOp(instr, state, up) - p += uintptr(elemWid) + p = unsafe.Pointer(uintptr(p) + elemWid) } } // decodeArray decodes an array and stores it through p, that is, p points to the zeroth element. // The length is an unsigned integer preceding the elements. Even though the length is redundant // (it's part of the type), it's a useful check and is included in the encoding. -func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintptr, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) { +func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p unsafe.Pointer, elemOp decOp, elemWid uintptr, length, indir, elemIndir int, ovfl error) { if indir > 0 { p = allocate(atyp, p, 1) // All but the last level has been allocated by dec.Indirect } @@ -591,7 +592,7 @@ func (dec *Decoder) decodeArray(atyp reflect.Type, state *decoderState, p uintpt // unlike the other items we can't use a pointer directly. func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, ovfl error) reflect.Value { instr := &decInstr{op, 0, indir, 0, ovfl} - up := unsafe.Pointer(unsafeAddr(v)) + up := unsafeAddr(v) if indir > 1 { up = decIndirect(up, indir) } @@ -603,7 +604,7 @@ func decodeIntoValue(state *decoderState, op decOp, indir int, v reflect.Value, // Maps are encoded as a length followed by key:value pairs. // Because the internals of maps are not visible to us, we must // use reflection rather than pointer magic. -func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p uintptr, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) { +func (dec *Decoder) decodeMap(mtyp reflect.Type, state *decoderState, p unsafe.Pointer, keyOp, elemOp decOp, indir, keyIndir, elemIndir int, ovfl error) { if indir > 0 { p = allocate(mtyp, p, 1) // All but the last level has been allocated by dec.Indirect } @@ -673,7 +674,7 @@ func (dec *Decoder) decodeSlice(atyp reflect.Type, state *decoderState, p uintpt hdrp.Cap = n } hdrp.Len = n - dec.decodeArrayHelper(state, hdrp.Data, elemOp, elemWid, n, elemIndir, ovfl) + dec.decodeArrayHelper(state, unsafe.Pointer(hdrp.Data), elemOp, elemWid, n, elemIndir, ovfl) } // ignoreSlice skips over the data for a slice value with no destination. @@ -693,7 +694,7 @@ func setInterfaceValue(ivalue reflect.Value, value reflect.Value) { // decodeInterface decodes an interface value and stores it through p. // Interfaces are encoded as the name of a concrete type followed by a value. // If the name is empty, the value is nil and no value is sent. -func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p uintptr, indir int) { +func (dec *Decoder) decodeInterface(ityp reflect.Type, state *decoderState, p unsafe.Pointer, indir int) { // Create a writable interface reflect.Value. We need one even for the nil case. ivalue := allocValue(ityp) // Read the name of the concrete type. @@ -767,15 +768,22 @@ func (dec *Decoder) ignoreInterface(state *decoderState) { // decodeGobDecoder decodes something implementing the GobDecoder interface. // The data is encoded as a byte slice. -func (dec *Decoder) decodeGobDecoder(state *decoderState, v reflect.Value) { +func (dec *Decoder) decodeGobDecoder(ut *userTypeInfo, state *decoderState, v reflect.Value) { // Read the bytes for the value. b := make([]byte, state.decodeUint()) _, err := state.b.Read(b) if err != nil { error_(err) } - // We know it's a GobDecoder, so just call the method directly. - err = v.Interface().(GobDecoder).GobDecode(b) + // We know it's one of these. + switch ut.externalDec { + case xGob: + err = v.Interface().(GobDecoder).GobDecode(b) + case xBinary: + err = v.Interface().(encoding.BinaryUnmarshaler).UnmarshalBinary(b) + case xText: + err = v.Interface().(encoding.TextUnmarshaler).UnmarshalText(b) + } if err != nil { error_(err) } @@ -825,9 +833,10 @@ var decIgnoreOpMap = map[typeId]decOp{ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProgress map[reflect.Type]*decOp) (*decOp, int) { ut := userType(rt) // If the type implements GobEncoder, we handle it without further processing. - if ut.isGobDecoder { + if ut.externalDec != 0 { return dec.gobDecodeOpFor(ut) } + // If this type is already in progress, it's a recursive type (e.g. map[string]*T). // Return the pointer to the op we're already building. if opPtr := inProgress[rt]; opPtr != nil { @@ -850,7 +859,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress) ovfl := overflow(name) op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { - state.dec.decodeArray(t, state, uintptr(p), *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) + state.dec.decodeArray(t, state, p, *elemOp, t.Elem().Size(), t.Len(), i.indir, elemIndir, ovfl) } case reflect.Map: @@ -860,8 +869,7 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), "element of "+name, inProgress) ovfl := overflow(name) op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { - up := unsafe.Pointer(p) - state.dec.decodeMap(t, state, uintptr(up), *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl) + state.dec.decodeMap(t, state, p, *keyOp, *elemOp, i.indir, keyIndir, elemIndir, ovfl) } case reflect.Slice: @@ -890,11 +898,11 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg } op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { // indirect through enginePtr to delay evaluation for recursive structs. - dec.decodeStruct(*enginePtr, userType(typ), uintptr(p), i.indir) + dec.decodeStruct(*enginePtr, userType(typ), p, i.indir) } case reflect.Interface: op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { - state.dec.decodeInterface(t, state, uintptr(p), i.indir) + state.dec.decodeInterface(t, state, p, i.indir) } } } @@ -955,7 +963,7 @@ func (dec *Decoder) decIgnoreOpFor(wireId typeId) decOp { state.dec.ignoreStruct(*enginePtr) } - case wire.GobEncoderT != nil: + case wire.GobEncoderT != nil, wire.BinaryMarshalerT != nil, wire.TextMarshalerT != nil: op = func(i *decInstr, state *decoderState, p unsafe.Pointer) { state.dec.ignoreGobDecoder(state) } @@ -994,7 +1002,7 @@ func (dec *Decoder) gobDecodeOpFor(ut *userTypeInfo) (*decOp, int) { } else { v = reflect.NewAt(rcvrType, p).Elem() } - state.dec.decodeGobDecoder(state, v) + state.dec.decodeGobDecoder(ut, state, v) } return &op, int(ut.indir) @@ -1011,12 +1019,18 @@ func (dec *Decoder) compatibleType(fr reflect.Type, fw typeId, inProgress map[re inProgress[fr] = fw ut := userType(fr) wire, ok := dec.wireType[fw] - // If fr is a GobDecoder, the wire type must be GobEncoder. - // And if fr is not a GobDecoder, the wire type must not be either. - if ut.isGobDecoder != (ok && wire.GobEncoderT != nil) { // the parentheses look odd but are correct. + // If wire was encoded with an encoding method, fr must have that method. + // And if not, it must not. + // At most one of the booleans in ut is set. + // We could possibly relax this constraint in the future in order to + // choose the decoding method using the data in the wireType. + // The parentheses look odd but are correct. + if (ut.externalDec == xGob) != (ok && wire.GobEncoderT != nil) || + (ut.externalDec == xBinary) != (ok && wire.BinaryMarshalerT != nil) || + (ut.externalDec == xText) != (ok && wire.TextMarshalerT != nil) { return false } - if ut.isGobDecoder { // This test trumps all others. + if ut.externalDec != 0 { // This test trumps all others. return true } switch t := ut.base; t.Kind() { @@ -1115,8 +1129,7 @@ func (dec *Decoder) compileIgnoreSingle(remoteId typeId) (engine *decEngine, err func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEngine, err error) { rt := ut.base srt := rt - if srt.Kind() != reflect.Struct || - ut.isGobDecoder { + if srt.Kind() != reflect.Struct || ut.externalDec != 0 { return dec.compileSingle(remoteId, ut) } var wireStruct *structType @@ -1224,14 +1237,14 @@ func (dec *Decoder) decodeValue(wireId typeId, val reflect.Value) { return } engine := *enginePtr - if st := base; st.Kind() == reflect.Struct && !ut.isGobDecoder { + if st := base; st.Kind() == reflect.Struct && ut.externalDec == 0 { if engine.numInstr == 0 && st.NumField() > 0 && len(dec.wireType[wireId].StructT.Field) > 0 { name := base.Name() errorf("type mismatch: no fields matched compiling decoder for %s", name) } - dec.decodeStruct(engine, ut, uintptr(unsafeAddr(val)), ut.indir) + dec.decodeStruct(engine, ut, unsafeAddr(val), ut.indir) } else { - dec.decodeSingle(engine, ut, uintptr(unsafeAddr(val))) + dec.decodeSingle(engine, ut, unsafeAddr(val)) } } @@ -1283,13 +1296,13 @@ func init() { // into existing structs or slices cannot be addressed, // so simulate it by returning a pointer to a copy. // Each call allocates once. -func unsafeAddr(v reflect.Value) uintptr { +func unsafeAddr(v reflect.Value) unsafe.Pointer { if v.CanAddr() { - return v.UnsafeAddr() + return unsafe.Pointer(v.UnsafeAddr()) } x := reflect.New(v.Type()).Elem() x.Set(v) - return x.UnsafeAddr() + return unsafe.Pointer(x.UnsafeAddr()) } // Gob depends on being able to take the address diff --git a/libgo/go/encoding/gob/doc.go b/libgo/go/encoding/gob/doc.go index 5bd61b1..28f0c05 100644 --- a/libgo/go/encoding/gob/doc.go +++ b/libgo/go/encoding/gob/doc.go @@ -8,6 +8,12 @@ Encoder (transmitter) and a Decoder (receiver). A typical use is transporting arguments and results of remote procedure calls (RPCs) such as those provided by package "rpc". +The implementation compiles a custom codec for each data type in the stream and +is most efficient when a single Encoder is used to transmit a stream of values, +amortizing the cost of compilation. + +Basics + A stream of gobs is self-describing. Each data item in the stream is preceded by a specification of its type, expressed in terms of a small set of predefined types. Pointers are not transmitted, but the things they point to are @@ -20,6 +26,8 @@ all type information is sent before it is needed. At the receive side, a Decoder retrieves values from the encoded stream and unpacks them into local variables. +Types and Values + The source and destination values/types need not correspond exactly. For structs, fields (identified by name) that are in the source but absent from the receiving variable will be ignored. Fields that are in the receiving variable but missing @@ -67,19 +75,29 @@ point values may be received into any floating point variable. However, the destination variable must be able to represent the value or the decode operation will fail. -Structs, arrays and slices are also supported. Structs encode and -decode only exported fields. Strings and arrays of bytes are supported -with a special, efficient representation (see below). When a slice -is decoded, if the existing slice has capacity the slice will be -extended in place; if not, a new array is allocated. Regardless, -the length of the resulting slice reports the number of elements -decoded. +Structs, arrays and slices are also supported. Structs encode and decode only +exported fields. Strings and arrays of bytes are supported with a special, +efficient representation (see below). When a slice is decoded, if the existing +slice has capacity the slice will be extended in place; if not, a new array is +allocated. Regardless, the length of the resulting slice reports the number of +elements decoded. + +Functions and channels will not be sent in a gob. Attempting to encode such a value +at top the level will fail. A struct field of chan or func type is treated exactly +like an unexported field and is ignored. + +Gob can encode a value of any type implementing the GobEncoder, +encoding.BinaryMarshaler, or encoding.TextMarshaler interfaces by calling the +corresponding method, in that order of preference. + +Gob can decode a value of any type implementing the GobDecoder, +encoding.BinaryUnmarshaler, or encoding.TextUnmarshaler interfaces by calling +the corresponding method, again in that order of preference. -Functions and channels cannot be sent in a gob. Attempting -to encode a value that contains one will fail. +Encoding Details -The rest of this comment documents the encoding, details that are not important -for most users. Details are presented bottom-up. +This section documents the encoding, details that are not important for most +users. Details are presented bottom-up. An unsigned integer is sent one of two ways. If it is less than 128, it is sent as a byte with that value. Otherwise it is sent as a minimal-length big-endian diff --git a/libgo/go/encoding/gob/encode.go b/libgo/go/encoding/gob/encode.go index ea37a6c..d158b64 100644 --- a/libgo/go/encoding/gob/encode.go +++ b/libgo/go/encoding/gob/encode.go @@ -6,6 +6,7 @@ package gob import ( "bytes" + "encoding" "math" "reflect" "unsafe" @@ -338,14 +339,14 @@ type encEngine struct { const singletonField = 0 // encodeSingle encodes a single top-level non-struct value. -func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintptr) { +func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep unsafe.Pointer) { state := enc.newEncoderState(b) state.fieldnum = singletonField // There is no surrounding struct to frame the transmission, so we must // generate data even if the item is zero. To do this, set sendZero. state.sendZero = true instr := &engine.instr[singletonField] - p := unsafe.Pointer(basep) // offset will be zero + p := basep // offset will be zero if instr.indir > 0 { if p = encIndirect(p, instr.indir); p == nil { return @@ -356,12 +357,12 @@ func (enc *Encoder) encodeSingle(b *bytes.Buffer, engine *encEngine, basep uintp } // encodeStruct encodes a single struct value. -func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintptr) { +func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep unsafe.Pointer) { state := enc.newEncoderState(b) state.fieldnum = -1 for i := 0; i < len(engine.instr); i++ { instr := &engine.instr[i] - p := unsafe.Pointer(basep + instr.offset) + p := unsafe.Pointer(uintptr(basep) + instr.offset) if instr.indir > 0 { if p = encIndirect(p, instr.indir); p == nil { continue @@ -373,22 +374,22 @@ func (enc *Encoder) encodeStruct(b *bytes.Buffer, engine *encEngine, basep uintp } // encodeArray encodes the array whose 0th element is at p. -func (enc *Encoder) encodeArray(b *bytes.Buffer, p uintptr, op encOp, elemWid uintptr, elemIndir int, length int) { +func (enc *Encoder) encodeArray(b *bytes.Buffer, p unsafe.Pointer, op encOp, elemWid uintptr, elemIndir int, length int) { state := enc.newEncoderState(b) state.fieldnum = -1 state.sendZero = true state.encodeUint(uint64(length)) for i := 0; i < length; i++ { elemp := p - up := unsafe.Pointer(elemp) if elemIndir > 0 { - if up = encIndirect(up, elemIndir); up == nil { + up := encIndirect(elemp, elemIndir) + if up == nil { errorf("encodeArray: nil element") } - elemp = uintptr(up) + elemp = up } - op(nil, state, unsafe.Pointer(elemp)) - p += uintptr(elemWid) + op(nil, state, elemp) + p = unsafe.Pointer(uintptr(p) + elemWid) } enc.freeEncoderState(state) } @@ -401,7 +402,7 @@ func encodeReflectValue(state *encoderState, v reflect.Value, op encOp, indir in if !v.IsValid() { errorf("encodeReflectValue: nil element") } - op(nil, state, unsafe.Pointer(unsafeAddr(v))) + op(nil, state, unsafeAddr(v)) } // encodeMap encodes a map as unsigned count followed by key:value pairs. @@ -474,7 +475,7 @@ func (enc *Encoder) encodeInterface(b *bytes.Buffer, iv reflect.Value) { enc.freeEncoderState(state) } -// isZero returns whether the value is the zero of its type. +// isZero reports whether the value is the zero of its type. func isZero(val reflect.Value) bool { switch val.Kind() { case reflect.Array: @@ -511,10 +512,20 @@ func isZero(val reflect.Value) bool { // encGobEncoder encodes a value that implements the GobEncoder interface. // The data is sent as a byte array. -func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, v reflect.Value) { +func (enc *Encoder) encodeGobEncoder(b *bytes.Buffer, ut *userTypeInfo, v reflect.Value) { // TODO: should we catch panics from the called method? - // We know it's a GobEncoder, so just call the method directly. - data, err := v.Interface().(GobEncoder).GobEncode() + + var data []byte + var err error + // We know it's one of these. + switch ut.externalEnc { + case xGob: + data, err = v.Interface().(GobEncoder).GobEncode() + case xBinary: + data, err = v.Interface().(encoding.BinaryMarshaler).MarshalBinary() + case xText: + data, err = v.Interface().(encoding.TextMarshaler).MarshalText() + } if err != nil { error_(err) } @@ -550,7 +561,7 @@ var encOpTable = [...]encOp{ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp) (*encOp, int) { ut := userType(rt) // If the type implements GobEncoder, we handle it without further processing. - if ut.isGobEncoder { + if ut.externalEnc != 0 { return enc.gobEncodeOpFor(ut) } // If this type is already in progress, it's a recursive type (e.g. map[string]*T). @@ -575,21 +586,21 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp break } // Slices have a header; we decode it to find the underlying array. - elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { slice := (*reflect.SliceHeader)(p) if !state.sendZero && slice.Len == 0 { return } state.update(i) - state.enc.encodeArray(state.b, slice.Data, *elemOp, t.Elem().Size(), indir, int(slice.Len)) + state.enc.encodeArray(state.b, unsafe.Pointer(slice.Data), *elemOp, t.Elem().Size(), elemIndir, int(slice.Len)) } case reflect.Array: // True arrays have size in the type. - elemOp, indir := enc.encOpFor(t.Elem(), inProgress) + elemOp, elemIndir := enc.encOpFor(t.Elem(), inProgress) op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { state.update(i) - state.enc.encodeArray(state.b, uintptr(p), *elemOp, t.Elem().Size(), indir, t.Len()) + state.enc.encodeArray(state.b, p, *elemOp, t.Elem().Size(), elemIndir, t.Len()) } case reflect.Map: keyOp, keyIndir := enc.encOpFor(t.Key(), inProgress) @@ -615,7 +626,7 @@ func (enc *Encoder) encOpFor(rt reflect.Type, inProgress map[reflect.Type]*encOp op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { state.update(i) // indirect through info to delay evaluation for recursive structs - state.enc.encodeStruct(state.b, info.encoder, uintptr(p)) + state.enc.encodeStruct(state.b, info.encoder, p) } case reflect.Interface: op = func(i *encInstr, state *encoderState, p unsafe.Pointer) { @@ -661,7 +672,7 @@ func (enc *Encoder) gobEncodeOpFor(ut *userTypeInfo) (*encOp, int) { return } state.update(i) - state.enc.encodeGobEncoder(state.b, v) + state.enc.encodeGobEncoder(state.b, ut, v) } return &op, int(ut.encIndir) // encIndir: op will get called with p == address of receiver. } @@ -672,14 +683,13 @@ func (enc *Encoder) compileEnc(ut *userTypeInfo) *encEngine { engine := new(encEngine) seen := make(map[reflect.Type]*encOp) rt := ut.base - if ut.isGobEncoder { + if ut.externalEnc != 0 { rt = ut.user } - if !ut.isGobEncoder && - srt.Kind() == reflect.Struct { + if ut.externalEnc == 0 && srt.Kind() == reflect.Struct { for fieldNum, wireFieldNum := 0, 0; fieldNum < srt.NumField(); fieldNum++ { f := srt.Field(fieldNum) - if !isExported(f.Name) { + if !isSent(&f) { continue } op, indir := enc.encOpFor(f.Type, seen) @@ -736,13 +746,13 @@ func (enc *Encoder) encode(b *bytes.Buffer, value reflect.Value, ut *userTypeInf defer catchError(&enc.err) engine := enc.lockAndGetEncEngine(ut) indir := ut.indir - if ut.isGobEncoder { + if ut.externalEnc != 0 { indir = int(ut.encIndir) } for i := 0; i < indir; i++ { value = reflect.Indirect(value) } - if !ut.isGobEncoder && value.Type().Kind() == reflect.Struct { + if ut.externalEnc == 0 && value.Type().Kind() == reflect.Struct { enc.encodeStruct(b, engine, unsafeAddr(value)) } else { enc.encodeSingle(b, engine, unsafeAddr(value)) diff --git a/libgo/go/encoding/gob/encoder.go b/libgo/go/encoding/gob/encoder.go index f669c3d..a3301c3 100644 --- a/libgo/go/encoding/gob/encoder.go +++ b/libgo/go/encoding/gob/encoder.go @@ -6,7 +6,6 @@ package gob import ( "bytes" - "errors" "io" "reflect" "sync" @@ -54,10 +53,6 @@ func (enc *Encoder) popWriter() { enc.w = enc.w[0 : len(enc.w)-1] } -func (enc *Encoder) badType(rt reflect.Type) { - enc.setError(errors.New("gob: can't encode type " + rt.String())) -} - func (enc *Encoder) setError(err error) { if enc.err == nil { // remember the first. enc.err = err @@ -135,7 +130,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp // sendType sends the type info to the other side, if necessary. func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { ut := userType(origt) - if ut.isGobEncoder { + if ut.externalEnc != 0 { // The rules are different: regardless of the underlying type's representation, // we need to tell the other side that the base type is a GobEncoder. return enc.sendActualType(w, state, ut, ut.base) @@ -163,8 +158,7 @@ func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Typ // structs must be sent so we know their fields. break case reflect.Chan, reflect.Func: - // Probably a bad field in a struct. - enc.badType(rt) + // If we get here, it's a field of a struct; ignore it. return } @@ -184,7 +178,7 @@ func (enc *Encoder) sendTypeDescriptor(w io.Writer, state *encoderState, ut *use // Make sure the type is known to the other side. // First, have we already sent this type? rt := ut.base - if ut.isGobEncoder { + if ut.externalEnc != 0 { rt = ut.user } if _, alreadySent := enc.sent[rt]; !alreadySent { diff --git a/libgo/go/encoding/gob/encoder_test.go b/libgo/go/encoding/gob/encoder_test.go index b684772c..4ecf51d 100644 --- a/libgo/go/encoding/gob/encoder_test.go +++ b/libgo/go/encoding/gob/encoder_test.go @@ -131,7 +131,7 @@ func TestBadData(t *testing.T) { corruptDataCheck("\x03now is the time for all good men", errBadType, t) } -// Types not supported by the Encoder. +// Types not supported at top level by the Encoder. var unsupportedValues = []interface{}{ make(chan int), func(a int) bool { return true }, @@ -662,19 +662,35 @@ func TestSequentialDecoder(t *testing.T) { } } -// Should be able to have unrepresentable fields (chan, func) as long as they -// are unexported. +// Should be able to have unrepresentable fields (chan, func, *chan etc.); we just ignore them. type Bug2 struct { - A int - b chan int -} - -func TestUnexportedChan(t *testing.T) { - b := Bug2{23, make(chan int)} - var stream bytes.Buffer - enc := NewEncoder(&stream) - if err := enc.Encode(b); err != nil { - t.Fatalf("error encoding unexported channel: %s", err) + A int + C chan int + CP *chan int + F func() + FPP **func() +} + +func TestChanFuncIgnored(t *testing.T) { + c := make(chan int) + f := func() {} + fp := &f + b0 := Bug2{23, c, &c, f, &fp} + var buf bytes.Buffer + enc := NewEncoder(&buf) + if err := enc.Encode(b0); err != nil { + t.Fatal("error encoding:", err) + } + var b1 Bug2 + err := NewDecoder(&buf).Decode(&b1) + if err != nil { + t.Fatal("decode:", err) + } + if b1.A != b0.A { + t.Fatalf("got %d want %d", b1.A, b0.A) + } + if b1.C != nil || b1.CP != nil || b1.F != nil || b1.FPP != nil { + t.Fatal("unexpected value for chan or func") } } diff --git a/libgo/go/encoding/gob/gobencdec_test.go b/libgo/go/encoding/gob/gobencdec_test.go index ddcd80b..301551d 100644 --- a/libgo/go/encoding/gob/gobencdec_test.go +++ b/libgo/go/encoding/gob/gobencdec_test.go @@ -34,6 +34,14 @@ type Gobber int type ValueGobber string // encodes with a value, decodes with a pointer. +type BinaryGobber int + +type BinaryValueGobber string + +type TextGobber int + +type TextValueGobber string + // The relevant methods func (g *ByteStruct) GobEncode() ([]byte, error) { @@ -101,6 +109,24 @@ func (g *Gobber) GobDecode(data []byte) error { return err } +func (g *BinaryGobber) MarshalBinary() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%d", *g)), nil +} + +func (g *BinaryGobber) UnmarshalBinary(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) + return err +} + +func (g *TextGobber) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%d", *g)), nil +} + +func (g *TextGobber) UnmarshalText(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%d", (*int)(g)) + return err +} + func (v ValueGobber) GobEncode() ([]byte, error) { return []byte(fmt.Sprintf("VALUE=%s", v)), nil } @@ -110,6 +136,24 @@ func (v *ValueGobber) GobDecode(data []byte) error { return err } +func (v BinaryValueGobber) MarshalBinary() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%s", v)), nil +} + +func (v *BinaryValueGobber) UnmarshalBinary(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) + return err +} + +func (v TextValueGobber) MarshalText() ([]byte, error) { + return []byte(fmt.Sprintf("VALUE=%s", v)), nil +} + +func (v *TextValueGobber) UnmarshalText(data []byte) error { + _, err := fmt.Sscanf(string(data), "VALUE=%s", (*string)(v)) + return err +} + // Structs that include GobEncodable fields. type GobTest0 struct { @@ -130,28 +174,42 @@ type GobTest2 struct { type GobTest3 struct { X int // guarantee we have something in common with GobTest* G *Gobber + B *BinaryGobber + T *TextGobber } type GobTest4 struct { - X int // guarantee we have something in common with GobTest* - V ValueGobber + X int // guarantee we have something in common with GobTest* + V ValueGobber + BV BinaryValueGobber + TV TextValueGobber } type GobTest5 struct { - X int // guarantee we have something in common with GobTest* - V *ValueGobber + X int // guarantee we have something in common with GobTest* + V *ValueGobber + BV *BinaryValueGobber + TV *TextValueGobber } type GobTest6 struct { - X int // guarantee we have something in common with GobTest* - V ValueGobber - W *ValueGobber + X int // guarantee we have something in common with GobTest* + V ValueGobber + W *ValueGobber + BV BinaryValueGobber + BW *BinaryValueGobber + TV TextValueGobber + TW *TextValueGobber } type GobTest7 struct { - X int // guarantee we have something in common with GobTest* - V *ValueGobber - W ValueGobber + X int // guarantee we have something in common with GobTest* + V *ValueGobber + W ValueGobber + BV *BinaryValueGobber + BW BinaryValueGobber + TV *TextValueGobber + TW TextValueGobber } type GobTestIgnoreEncoder struct { @@ -198,7 +256,9 @@ func TestGobEncoderField(t *testing.T) { // Now a field that's not a structure. b.Reset() gobber := Gobber(23) - err = enc.Encode(GobTest3{17, &gobber}) + bgobber := BinaryGobber(24) + tgobber := TextGobber(25) + err = enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber}) if err != nil { t.Fatal("encode error:", err) } @@ -207,7 +267,7 @@ func TestGobEncoderField(t *testing.T) { if err != nil { t.Fatal("decode error:", err) } - if *y.G != 23 { + if *y.G != 23 || *y.B != 24 || *y.T != 25 { t.Errorf("expected '23 got %d", *y.G) } } @@ -357,7 +417,7 @@ func TestGobEncoderValueEncoder(t *testing.T) { // first, string in field to byte in field b := new(bytes.Buffer) enc := NewEncoder(b) - err := enc.Encode(GobTest4{17, ValueGobber("hello")}) + err := enc.Encode(GobTest4{17, ValueGobber("hello"), BinaryValueGobber("Καλημέρα"), TextValueGobber("こんにちは")}) if err != nil { t.Fatal("encode error:", err) } @@ -367,8 +427,8 @@ func TestGobEncoderValueEncoder(t *testing.T) { if err != nil { t.Fatal("decode error:", err) } - if *x.V != "hello" { - t.Errorf("expected `hello` got %s", x.V) + if *x.V != "hello" || *x.BV != "Καλημέρα" || *x.TV != "こんにちは" { + t.Errorf("expected `hello` got %s", *x.V) } } @@ -377,13 +437,17 @@ func TestGobEncoderValueEncoder(t *testing.T) { func TestGobEncoderValueThenPointer(t *testing.T) { v := ValueGobber("forty-two") w := ValueGobber("six-by-nine") + bv := BinaryValueGobber("1nanocentury") + bw := BinaryValueGobber("πseconds") + tv := TextValueGobber("gravitationalacceleration") + tw := TextValueGobber("π²ft/s²") // this was a bug: encoding a GobEncoder by value before a GobEncoder // pointer would cause duplicate type definitions to be sent. b := new(bytes.Buffer) enc := NewEncoder(b) - if err := enc.Encode(GobTest6{42, v, &w}); err != nil { + if err := enc.Encode(GobTest6{42, v, &w, bv, &bw, tv, &tw}); err != nil { t.Fatal("encode error:", err) } dec := NewDecoder(b) @@ -391,6 +455,7 @@ func TestGobEncoderValueThenPointer(t *testing.T) { if err := dec.Decode(x); err != nil { t.Fatal("decode error:", err) } + if got, want := x.V, v; got != want { t.Errorf("v = %q, want %q", got, want) } @@ -399,6 +464,24 @@ func TestGobEncoderValueThenPointer(t *testing.T) { } else if *got != want { t.Errorf("w = %q, want %q", *got, want) } + + if got, want := x.BV, bv; got != want { + t.Errorf("bv = %q, want %q", got, want) + } + if got, want := x.BW, bw; got == nil { + t.Errorf("bw = nil, want %q", want) + } else if *got != want { + t.Errorf("bw = %q, want %q", *got, want) + } + + if got, want := x.TV, tv; got != want { + t.Errorf("tv = %q, want %q", got, want) + } + if got, want := x.TW, tw; got == nil { + t.Errorf("tw = nil, want %q", want) + } else if *got != want { + t.Errorf("tw = %q, want %q", *got, want) + } } // Test that we can use a pointer then a value type of a GobEncoder @@ -406,10 +489,14 @@ func TestGobEncoderValueThenPointer(t *testing.T) { func TestGobEncoderPointerThenValue(t *testing.T) { v := ValueGobber("forty-two") w := ValueGobber("six-by-nine") + bv := BinaryValueGobber("1nanocentury") + bw := BinaryValueGobber("πseconds") + tv := TextValueGobber("gravitationalacceleration") + tw := TextValueGobber("π²ft/s²") b := new(bytes.Buffer) enc := NewEncoder(b) - if err := enc.Encode(GobTest7{42, &v, w}); err != nil { + if err := enc.Encode(GobTest7{42, &v, w, &bv, bw, &tv, tw}); err != nil { t.Fatal("encode error:", err) } dec := NewDecoder(b) @@ -417,14 +504,33 @@ func TestGobEncoderPointerThenValue(t *testing.T) { if err := dec.Decode(x); err != nil { t.Fatal("decode error:", err) } + if got, want := x.V, v; got == nil { t.Errorf("v = nil, want %q", want) } else if *got != want { - t.Errorf("v = %q, want %q", got, want) + t.Errorf("v = %q, want %q", *got, want) } if got, want := x.W, w; got != want { t.Errorf("w = %q, want %q", got, want) } + + if got, want := x.BV, bv; got == nil { + t.Errorf("bv = nil, want %q", want) + } else if *got != want { + t.Errorf("bv = %q, want %q", *got, want) + } + if got, want := x.BW, bw; got != want { + t.Errorf("bw = %q, want %q", got, want) + } + + if got, want := x.TV, tv; got == nil { + t.Errorf("tv = nil, want %q", want) + } else if *got != want { + t.Errorf("tv = %q, want %q", *got, want) + } + if got, want := x.TW, tw; got != want { + t.Errorf("tw = %q, want %q", got, want) + } } func TestGobEncoderFieldTypeError(t *testing.T) { @@ -521,7 +627,9 @@ func TestGobEncoderIgnoreNonStructField(t *testing.T) { // First a field that's a structure. enc := NewEncoder(b) gobber := Gobber(23) - err := enc.Encode(GobTest3{17, &gobber}) + bgobber := BinaryGobber(24) + tgobber := TextGobber(25) + err := enc.Encode(GobTest3{17, &gobber, &bgobber, &tgobber}) if err != nil { t.Fatal("encode error:", err) } diff --git a/libgo/go/encoding/gob/timing_test.go b/libgo/go/encoding/gob/timing_test.go index f589675..9fbb0ac 100644 --- a/libgo/go/encoding/gob/timing_test.go +++ b/libgo/go/encoding/gob/timing_test.go @@ -6,7 +6,6 @@ package gob import ( "bytes" - "fmt" "io" "os" "runtime" @@ -50,6 +49,9 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) { } func TestCountEncodeMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -66,10 +68,15 @@ func TestCountEncodeMallocs(t *testing.T) { t.Fatal("encode:", err) } }) - fmt.Printf("mallocs per encode of type Bench: %v\n", allocs) + if allocs != 0 { + t.Fatalf("mallocs per encode of type Bench: %v; wanted 0\n", allocs) + } } func TestCountDecodeMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -96,5 +103,7 @@ func TestCountDecodeMallocs(t *testing.T) { t.Fatal("decode:", err) } }) - fmt.Printf("mallocs per decode of type Bench: %v\n", allocs) + if allocs != 3 { + t.Fatalf("mallocs per decode of type Bench: %v; wanted 3\n", allocs) + } } diff --git a/libgo/go/encoding/gob/type.go b/libgo/go/encoding/gob/type.go index 7fa0b499..65bf17b 100644 --- a/libgo/go/encoding/gob/type.go +++ b/libgo/go/encoding/gob/type.go @@ -5,6 +5,7 @@ package gob import ( + "encoding" "errors" "fmt" "os" @@ -18,14 +19,21 @@ import ( // to the package. It's computed once and stored in a map keyed by reflection // type. type userTypeInfo struct { - user reflect.Type // the type the user handed us - base reflect.Type // the base type after all indirections - indir int // number of indirections to reach the base type - isGobEncoder bool // does the type implement GobEncoder? - isGobDecoder bool // does the type implement GobDecoder? - encIndir int8 // number of indirections to reach the receiver type; may be negative - decIndir int8 // number of indirections to reach the receiver type; may be negative -} + user reflect.Type // the type the user handed us + base reflect.Type // the base type after all indirections + indir int // number of indirections to reach the base type + externalEnc int // xGob, xBinary, or xText + externalDec int // xGob, xBinary or xText + encIndir int8 // number of indirections to reach the receiver type; may be negative + decIndir int8 // number of indirections to reach the receiver type; may be negative +} + +// externalEncoding bits +const ( + xGob = 1 + iota // GobEncoder or GobDecoder + xBinary // encoding.BinaryMarshaler or encoding.BinaryUnmarshaler + xText // encoding.TextMarshaler or encoding.TextUnmarshaler +) var ( // Protected by an RWMutex because we read it a lot and write @@ -75,15 +83,34 @@ func validUserType(rt reflect.Type) (ut *userTypeInfo, err error) { } ut.indir++ } - ut.isGobEncoder, ut.encIndir = implementsInterface(ut.user, gobEncoderInterfaceType) - ut.isGobDecoder, ut.decIndir = implementsInterface(ut.user, gobDecoderInterfaceType) + + if ok, indir := implementsInterface(ut.user, gobEncoderInterfaceType); ok { + ut.externalEnc, ut.encIndir = xGob, indir + } else if ok, indir := implementsInterface(ut.user, binaryMarshalerInterfaceType); ok { + ut.externalEnc, ut.encIndir = xBinary, indir + } else if ok, indir := implementsInterface(ut.user, textMarshalerInterfaceType); ok { + ut.externalEnc, ut.encIndir = xText, indir + } + + if ok, indir := implementsInterface(ut.user, gobDecoderInterfaceType); ok { + ut.externalDec, ut.decIndir = xGob, indir + } else if ok, indir := implementsInterface(ut.user, binaryUnmarshalerInterfaceType); ok { + ut.externalDec, ut.decIndir = xBinary, indir + } else if ok, indir := implementsInterface(ut.user, textUnmarshalerInterfaceType); ok { + ut.externalDec, ut.decIndir = xText, indir + } + userTypeCache[rt] = ut return } var ( - gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem() - gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem() + gobEncoderInterfaceType = reflect.TypeOf((*GobEncoder)(nil)).Elem() + gobDecoderInterfaceType = reflect.TypeOf((*GobDecoder)(nil)).Elem() + binaryMarshalerInterfaceType = reflect.TypeOf((*encoding.BinaryMarshaler)(nil)).Elem() + binaryUnmarshalerInterfaceType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem() + textMarshalerInterfaceType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() + textUnmarshalerInterfaceType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() ) // implementsInterface reports whether the type implements the @@ -412,7 +439,7 @@ func newStructType(name string) *structType { // works through typeIds and userTypeInfos alone. func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, error) { // Does this type implement GobEncoder? - if ut.isGobEncoder { + if ut.externalEnc != 0 { return newGobEncoderType(name), nil } var err error @@ -499,7 +526,7 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, err idToType[st.id()] = st for i := 0; i < t.NumField(); i++ { f := t.Field(i) - if !isExported(f.Name) { + if !isSent(&f) { continue } typ := userType(f.Type).base @@ -534,6 +561,25 @@ func isExported(name string) bool { return unicode.IsUpper(rune) } +// isSent reports whether this struct field is to be transmitted. +// It will be transmitted only if it is exported and not a chan or func field +// or pointer to chan or func. +func isSent(field *reflect.StructField) bool { + if !isExported(field.Name) { + return false + } + // If the field is a chan or func or pointer thereto, don't send it. + // That is, treat it like an unexported field. + typ := field.Type + for typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + if typ.Kind() == reflect.Chan || typ.Kind() == reflect.Func { + return false + } + return true +} + // getBaseType returns the Gob type describing the given reflect.Type's base type. // typeLock must be held. func getBaseType(name string, rt reflect.Type) (gobType, error) { @@ -593,11 +639,13 @@ func bootstrapType(name string, e interface{}, expect typeId) typeId { // To maintain binary compatibility, if you extend this type, always put // the new fields last. type wireType struct { - ArrayT *arrayType - SliceT *sliceType - StructT *structType - MapT *mapType - GobEncoderT *gobEncoderType + ArrayT *arrayType + SliceT *sliceType + StructT *structType + MapT *mapType + GobEncoderT *gobEncoderType + BinaryMarshalerT *gobEncoderType + TextMarshalerT *gobEncoderType } func (w *wireType) string() string { @@ -616,6 +664,10 @@ func (w *wireType) string() string { return w.MapT.Name case w.GobEncoderT != nil: return w.GobEncoderT.Name + case w.BinaryMarshalerT != nil: + return w.BinaryMarshalerT.Name + case w.TextMarshalerT != nil: + return w.TextMarshalerT.Name } return unknown } @@ -631,7 +683,7 @@ var typeInfoMap = make(map[reflect.Type]*typeInfo) // protected by typeLock // typeLock must be held. func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { rt := ut.base - if ut.isGobEncoder { + if ut.externalEnc != 0 { // We want the user type, not the base type. rt = ut.user } @@ -646,12 +698,20 @@ func getTypeInfo(ut *userTypeInfo) (*typeInfo, error) { } info.id = gt.id() - if ut.isGobEncoder { + if ut.externalEnc != 0 { userType, err := getType(rt.Name(), ut, rt) if err != nil { return nil, err } - info.wire = &wireType{GobEncoderT: userType.id().gobType().(*gobEncoderType)} + gt := userType.id().gobType().(*gobEncoderType) + switch ut.externalEnc { + case xGob: + info.wire = &wireType{GobEncoderT: gt} + case xBinary: + info.wire = &wireType{BinaryMarshalerT: gt} + case xText: + info.wire = &wireType{TextMarshalerT: gt} + } typeInfoMap[ut.user] = info return info, nil } diff --git a/libgo/go/encoding/json/decode.go b/libgo/go/encoding/json/decode.go index 62ac294..458fb39 100644 --- a/libgo/go/encoding/json/decode.go +++ b/libgo/go/encoding/json/decode.go @@ -8,6 +8,7 @@ package json import ( + "encoding" "encoding/base64" "errors" "fmt" @@ -37,9 +38,7 @@ import ( // keys to the keys used by Marshal (either the struct field name or its tag), // preferring an exact match but also accepting a case-insensitive match. // -// To unmarshal JSON into an interface value, Unmarshal unmarshals -// the JSON into the concrete value contained in the interface value. -// If the interface value is nil, that is, has no concrete value stored in it, +// To unmarshal JSON into an interface value, // Unmarshal stores one of these in the interface value: // // bool, for JSON booleans @@ -293,7 +292,7 @@ func (d *decodeState) value(v reflect.Value) { // until it gets to a non-pointer. // if it encounters an Unmarshaler, indirect stops and returns that. // if decodingNull is true, indirect stops at the last pointer so it can be set to nil. -func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, reflect.Value) { +func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, encoding.TextUnmarshaler, reflect.Value) { // If v is a named type and is addressable, // start with its address, so that if the type has pointer methods, // we find them. @@ -322,28 +321,38 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler, v.Set(reflect.New(v.Type().Elem())) } if v.Type().NumMethod() > 0 { - if unmarshaler, ok := v.Interface().(Unmarshaler); ok { - return unmarshaler, reflect.Value{} + if u, ok := v.Interface().(Unmarshaler); ok { + return u, nil, reflect.Value{} + } + if u, ok := v.Interface().(encoding.TextUnmarshaler); ok { + return nil, u, reflect.Value{} } } v = v.Elem() } - return nil, v + return nil, nil, v } // array consumes an array from d.data[d.off-1:], decoding into the value v. // the first byte of the array ('[') has been read already. func (d *decodeState) array(v reflect.Value) { // Check for unmarshaler. - unmarshaler, pv := d.indirect(v, false) - if unmarshaler != nil { + u, ut, pv := d.indirect(v, false) + if u != nil { d.off-- - err := unmarshaler.UnmarshalJSON(d.next()) + err := u.UnmarshalJSON(d.next()) if err != nil { d.error(err) } return } + if ut != nil { + d.saveError(&UnmarshalTypeError{"array", v.Type()}) + d.off-- + d.next() + return + } + v = pv // Check type of target. @@ -434,15 +443,21 @@ func (d *decodeState) array(v reflect.Value) { // the first byte of the object ('{') has been read already. func (d *decodeState) object(v reflect.Value) { // Check for unmarshaler. - unmarshaler, pv := d.indirect(v, false) - if unmarshaler != nil { + u, ut, pv := d.indirect(v, false) + if u != nil { d.off-- - err := unmarshaler.UnmarshalJSON(d.next()) + err := u.UnmarshalJSON(d.next()) if err != nil { d.error(err) } return } + if ut != nil { + d.saveError(&UnmarshalTypeError{"object", v.Type()}) + d.off-- + d.next() // skip over { } in input + return + } v = pv // Decoding into nil interface? Switch to non-reflect code. @@ -611,14 +626,37 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool return } wantptr := item[0] == 'n' // null - unmarshaler, pv := d.indirect(v, wantptr) - if unmarshaler != nil { - err := unmarshaler.UnmarshalJSON(item) + u, ut, pv := d.indirect(v, wantptr) + if u != nil { + err := u.UnmarshalJSON(item) + if err != nil { + d.error(err) + } + return + } + if ut != nil { + if item[0] != '"' { + if fromQuoted { + d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.saveError(&UnmarshalTypeError{"string", v.Type()}) + } + } + s, ok := unquoteBytes(item) + if !ok { + if fromQuoted { + d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type())) + } else { + d.error(errPhase) + } + } + err := ut.UnmarshalText(s) if err != nil { d.error(err) } return } + v = pv switch c := item[0]; c { diff --git a/libgo/go/encoding/json/decode_test.go b/libgo/go/encoding/json/decode_test.go index f845f69..22c5f89 100644 --- a/libgo/go/encoding/json/decode_test.go +++ b/libgo/go/encoding/json/decode_test.go @@ -6,6 +6,7 @@ package json import ( "bytes" + "encoding" "fmt" "image" "reflect" @@ -50,8 +51,6 @@ type tx struct { x int } -var txType = reflect.TypeOf((*tx)(nil)).Elem() - // A type that can unmarshal itself. type unmarshaler struct { @@ -59,7 +58,7 @@ type unmarshaler struct { } func (u *unmarshaler) UnmarshalJSON(b []byte) error { - *u = unmarshaler{true} // All we need to see that UnmarshalJson is called. + *u = unmarshaler{true} // All we need to see that UnmarshalJSON is called. return nil } @@ -67,6 +66,26 @@ type ustruct struct { M unmarshaler } +type unmarshalerText struct { + T bool +} + +// needed for re-marshaling tests +func (u *unmarshalerText) MarshalText() ([]byte, error) { + return []byte(""), nil +} + +func (u *unmarshalerText) UnmarshalText(b []byte) error { + *u = unmarshalerText{true} // All we need to see that UnmarshalText is called. + return nil +} + +var _ encoding.TextUnmarshaler = (*unmarshalerText)(nil) + +type ustructText struct { + M unmarshalerText +} + var ( um0, um1 unmarshaler // target2 of unmarshaling ump = &um1 @@ -74,6 +93,13 @@ var ( umslice = []unmarshaler{{true}} umslicep = new([]unmarshaler) umstruct = ustruct{unmarshaler{true}} + + um0T, um1T unmarshalerText // target2 of unmarshaling + umpT = &um1T + umtrueT = unmarshalerText{true} + umsliceT = []unmarshalerText{{true}} + umslicepT = new([]unmarshalerText) + umstructT = ustructText{unmarshalerText{true}} ) // Test data structures for anonymous fields. @@ -184,6 +210,12 @@ type Ambig struct { Second int `json:"Hello"` } +type XYZ struct { + X interface{} + Y interface{} + Z interface{} +} + var unmarshalTests = []unmarshalTest{ // basic types {in: `true`, ptr: new(bool), out: true}, @@ -263,6 +295,13 @@ var unmarshalTests = []unmarshalTest{ {in: `[{"T":false}]`, ptr: &umslicep, out: &umslice}, {in: `{"M":{"T":false}}`, ptr: &umstruct, out: umstruct}, + // UnmarshalText interface test + {in: `"X"`, ptr: &um0T, out: umtrueT}, // use "false" so test will fail if custom unmarshaler is not called + {in: `"X"`, ptr: &umpT, out: &umtrueT}, + {in: `["X"]`, ptr: &umsliceT, out: umsliceT}, + {in: `["X"]`, ptr: &umslicepT, out: &umsliceT}, + {in: `{"M":"X"}`, ptr: &umstructT, out: umstructT}, + { in: `{ "Level0": 1, @@ -391,17 +430,23 @@ func TestMarshal(t *testing.T) { } } +var badUTF8 = []struct { + in, out string +}{ + {"hello\xffworld", `"hello\ufffdworld"`}, + {"", `""`}, + {"\xff", `"\ufffd"`}, + {"\xff\xff", `"\ufffd\ufffd"`}, + {"a\xffb", `"a\ufffdb"`}, + {"\xe6\x97\xa5\xe6\x9c\xac\xff\xaa\x9e", `"日本\ufffd\ufffd\ufffd"`}, +} + func TestMarshalBadUTF8(t *testing.T) { - s := "hello\xffworld" - b, err := Marshal(s) - if err == nil { - t.Fatal("Marshal bad UTF8: no error") - } - if len(b) != 0 { - t.Fatal("Marshal returned data") - } - if _, ok := err.(*InvalidUTF8Error); !ok { - t.Fatalf("Marshal did not return InvalidUTF8Error: %T %v", err, err) + for _, tt := range badUTF8 { + b, err := Marshal(tt.in) + if string(b) != tt.out || err != nil { + t.Errorf("Marshal(%q) = %#q, %v, want %#q, nil", tt.in, b, err, tt.out) + } } } @@ -417,6 +462,45 @@ func TestMarshalNumberZeroVal(t *testing.T) { } } +func TestMarshalEmbeds(t *testing.T) { + top := &Top{ + Level0: 1, + Embed0: Embed0{ + Level1b: 2, + Level1c: 3, + }, + Embed0a: &Embed0a{ + Level1a: 5, + Level1b: 6, + }, + Embed0b: &Embed0b{ + Level1a: 8, + Level1b: 9, + Level1c: 10, + Level1d: 11, + Level1e: 12, + }, + Loop: Loop{ + Loop1: 13, + Loop2: 14, + }, + Embed0p: Embed0p{ + Point: image.Point{X: 15, Y: 16}, + }, + Embed0q: Embed0q{ + Point: Point{Z: 17}, + }, + } + b, err := Marshal(top) + if err != nil { + t.Fatal(err) + } + want := "{\"Level0\":1,\"Level1b\":2,\"Level1c\":3,\"Level1a\":5,\"LEVEL1B\":6,\"e\":{\"Level1a\":8,\"Level1b\":9,\"Level1c\":10,\"Level1d\":11,\"x\":12},\"Loop1\":13,\"Loop2\":14,\"X\":15,\"Y\":16,\"Z\":17}" + if string(b) != want { + t.Errorf("Wrong marshal result.\n got: %q\nwant: %q", b, want) + } +} + func TestUnmarshal(t *testing.T) { for i, tt := range unmarshalTests { var scan scanner @@ -432,7 +516,7 @@ func TestUnmarshal(t *testing.T) { } // v = new(right-type) v := reflect.New(reflect.TypeOf(tt.ptr).Elem()) - dec := NewDecoder(bytes.NewBuffer(in)) + dec := NewDecoder(bytes.NewReader(in)) if tt.useNumber { dec.UseNumber() } @@ -457,16 +541,18 @@ func TestUnmarshal(t *testing.T) { continue } vv := reflect.New(reflect.TypeOf(tt.ptr).Elem()) - dec = NewDecoder(bytes.NewBuffer(enc)) + dec = NewDecoder(bytes.NewReader(enc)) if tt.useNumber { dec.UseNumber() } if err := dec.Decode(vv.Interface()); err != nil { - t.Errorf("#%d: error re-unmarshaling: %v", i, err) + t.Errorf("#%d: error re-unmarshaling %#q: %v", i, enc, err) continue } if !reflect.DeepEqual(v.Elem().Interface(), vv.Elem().Interface()) { t.Errorf("#%d: mismatch\nhave: %#+v\nwant: %#+v", i, v.Elem().Interface(), vv.Elem().Interface()) + t.Errorf(" In: %q", strings.Map(noSpace, string(in))) + t.Errorf("Marshal: %q", strings.Map(noSpace, string(enc))) continue } } @@ -568,14 +654,14 @@ func TestUnmarshalPtrPtr(t *testing.T) { } func TestEscape(t *testing.T) { - const input = `"foobar"<html>` - const expected = `"\"foobar\"\u003chtml\u003e"` + const input = `"foobar"<html>` + " [\u2028 \u2029]" + const expected = `"\"foobar\"\u003chtml\u003e [\u2028 \u2029]"` b, err := Marshal(input) if err != nil { t.Fatalf("Marshal error: %v", err) } if s := string(b); s != expected { - t.Errorf("Encoding of [%s] was [%s], want [%s]", input, s, expected) + t.Errorf("Encoding of [%s]:\n got [%s]\nwant [%s]", input, s, expected) } } @@ -934,15 +1020,20 @@ func TestRefUnmarshal(t *testing.T) { // Ref is defined in encode_test.go. R0 Ref R1 *Ref + R2 RefText + R3 *RefText } want := S{ R0: 12, R1: new(Ref), + R2: 13, + R3: new(RefText), } *want.R1 = 12 + *want.R3 = 13 var got S - if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref"}`), &got); err != nil { + if err := Unmarshal([]byte(`{"R0":"ref","R1":"ref","R2":"ref","R3":"ref"}`), &got); err != nil { t.Fatalf("Unmarshal: %v", err) } if !reflect.DeepEqual(got, want) { @@ -1064,7 +1155,6 @@ func TestUnmarshalNulls(t *testing.T) { func TestStringKind(t *testing.T) { type stringKind string - type aMap map[stringKind]int var m1, m2 map[stringKind]int m1 = map[stringKind]int{ @@ -1191,3 +1281,38 @@ func TestSkipArrayObjects(t *testing.T) { t.Errorf("got error %q, want nil", err) } } + +// Test semantics of pre-filled struct fields and pre-filled map fields. +// Issue 4900. +func TestPrefilled(t *testing.T) { + ptrToMap := func(m map[string]interface{}) *map[string]interface{} { return &m } + + // Values here change, cannot reuse table across runs. + var prefillTests = []struct { + in string + ptr interface{} + out interface{} + }{ + { + in: `{"X": 1, "Y": 2}`, + ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5}, + out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5}, + }, + { + in: `{"X": 1, "Y": 2}`, + ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}), + out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}), + }, + } + + for _, tt := range prefillTests { + ptrstr := fmt.Sprintf("%v", tt.ptr) + err := Unmarshal([]byte(tt.in), tt.ptr) // tt.ptr edited here + if err != nil { + t.Errorf("Unmarshal: %v", err) + } + if !reflect.DeepEqual(tt.ptr, tt.out) { + t.Errorf("Unmarshal(%#q, %s): have %v, want %v", tt.in, ptrstr, tt.ptr, tt.out) + } + } +} diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 85727ba..7d6c71d 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -12,6 +12,7 @@ package json import ( "bytes" + "encoding" "encoding/base64" "math" "reflect" @@ -149,14 +150,14 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { return buf.Bytes(), nil } -// HTMLEscape appends to dst the JSON-encoded src with <, >, and & -// characters inside string literals changed to \u003c, \u003e, \u0026 +// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 +// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 // so that the JSON will be safe to embed inside HTML <script> tags. // For historical reasons, web browsers don't honor standard HTML // escaping within <script> tags, so an alternative JSON encoding must // be used. func HTMLEscape(dst *bytes.Buffer, src []byte) { - // < > & can only appear in string literals, + // The characters can only appear in string literals, // so just scan the string one byte at a time. start := 0 for i, c := range src { @@ -169,6 +170,15 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) { dst.WriteByte(hex[c&0xF]) start = i + 1 } + // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). + if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { + if start < i { + dst.Write(src[start:i]) + } + dst.WriteString(`\u202`) + dst.WriteByte(hex[src[i+2]&0xF]) + start = i + 3 + } } if start < len(src) { dst.Write(src[start:]) @@ -200,8 +210,12 @@ func (e *UnsupportedValueError) Error() string { return "json: unsupported value: " + e.Str } -// An InvalidUTF8Error is returned by Marshal when attempting -// to encode a string value with invalid UTF-8 sequences. +// Before Go 1.2, an InvalidUTF8Error was returned by Marshal when +// attempting to encode a string value with invalid UTF-8 sequences. +// As of Go 1.2, Marshal instead coerces the string to valid UTF-8 by +// replacing invalid bytes with the Unicode replacement rune U+FFFD. +// This error is no longer generated but is kept for backwards compatibility +// with programs that might mention it. type InvalidUTF8Error struct { S string // the whole string value that caused the error } @@ -227,12 +241,35 @@ type encodeState struct { scratch [64]byte } +// TODO(bradfitz): use a sync.Cache here +var encodeStatePool = make(chan *encodeState, 8) + +func newEncodeState() *encodeState { + select { + case e := <-encodeStatePool: + e.Reset() + return e + default: + return new(encodeState) + } +} + +func putEncodeState(e *encodeState) { + select { + case encodeStatePool <- e: + default: + } +} + func (e *encodeState) marshal(v interface{}) (err error) { defer func() { if r := recover(); r != nil { if _, ok := r.(runtime.Error); ok { panic(r) } + if s, ok := r.(string); ok { + panic(s) + } err = r.(error) } }() @@ -265,186 +302,438 @@ func isEmptyValue(v reflect.Value) bool { } func (e *encodeState) reflectValue(v reflect.Value) { - e.reflectValueQuoted(v, false) + valueEncoder(v)(e, v, false) +} + +type encoderFunc func(e *encodeState, v reflect.Value, quoted bool) + +var encoderCache struct { + sync.RWMutex + m map[reflect.Type]encoderFunc } -// reflectValueQuoted writes the value in v to the output. -// If quoted is true, the serialization is wrapped in a JSON string. -func (e *encodeState) reflectValueQuoted(v reflect.Value, quoted bool) { +func valueEncoder(v reflect.Value) encoderFunc { if !v.IsValid() { - e.WriteString("null") - return + return invalidValueEncoder } + return typeEncoder(v.Type()) +} - m, ok := v.Interface().(Marshaler) - if !ok { - // T doesn't match the interface. Check against *T too. - if v.Kind() != reflect.Ptr && v.CanAddr() { - m, ok = v.Addr().Interface().(Marshaler) - if ok { - v = v.Addr() - } - } +func typeEncoder(t reflect.Type) encoderFunc { + encoderCache.RLock() + f := encoderCache.m[t] + encoderCache.RUnlock() + if f != nil { + return f + } + + // To deal with recursive types, populate the map with an + // indirect func before we build it. This type waits on the + // real func (f) to be ready and then calls it. This indirect + // func is only used for recursive types. + encoderCache.Lock() + if encoderCache.m == nil { + encoderCache.m = make(map[reflect.Type]encoderFunc) + } + var wg sync.WaitGroup + wg.Add(1) + encoderCache.m[t] = func(e *encodeState, v reflect.Value, quoted bool) { + wg.Wait() + f(e, v, quoted) + } + encoderCache.Unlock() + + // Compute fields without lock. + // Might duplicate effort but won't hold other computations back. + f = newTypeEncoder(t, true) + wg.Done() + encoderCache.Lock() + encoderCache.m[t] = f + encoderCache.Unlock() + return f +} + +var ( + marshalerType = reflect.TypeOf(new(Marshaler)).Elem() + textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() +) + +// newTypeEncoder constructs an encoderFunc for a type. +// The returned encoder only checks CanAddr when allowAddr is true. +func newTypeEncoder(t reflect.Type, allowAddr bool) encoderFunc { + if t.Implements(marshalerType) { + return marshalerEncoder } - if ok && (v.Kind() != reflect.Ptr || !v.IsNil()) { - b, err := m.MarshalJSON() - if err == nil { - // copy JSON into buffer, checking validity. - err = compact(&e.Buffer, b, true) + if t.Kind() != reflect.Ptr && allowAddr { + if reflect.PtrTo(t).Implements(marshalerType) { + return newCondAddrEncoder(addrMarshalerEncoder, newTypeEncoder(t, false)) } - if err != nil { - e.error(&MarshalerError{v.Type(), err}) + } + + if t.Implements(textMarshalerType) { + return textMarshalerEncoder + } + if t.Kind() != reflect.Ptr && allowAddr { + if reflect.PtrTo(t).Implements(textMarshalerType) { + return newCondAddrEncoder(addrTextMarshalerEncoder, newTypeEncoder(t, false)) } + } + + switch t.Kind() { + case reflect.Bool: + return boolEncoder + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intEncoder + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintEncoder + case reflect.Float32: + return float32Encoder + case reflect.Float64: + return float64Encoder + case reflect.String: + return stringEncoder + case reflect.Interface: + return interfaceEncoder + case reflect.Struct: + return newStructEncoder(t) + case reflect.Map: + return newMapEncoder(t) + case reflect.Slice: + return newSliceEncoder(t) + case reflect.Array: + return newArrayEncoder(t) + case reflect.Ptr: + return newPtrEncoder(t) + default: + return unsupportedTypeEncoder + } +} + +func invalidValueEncoder(e *encodeState, v reflect.Value, quoted bool) { + e.WriteString("null") +} + +func marshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { + if v.Kind() == reflect.Ptr && v.IsNil() { + e.WriteString("null") + return + } + m := v.Interface().(Marshaler) + b, err := m.MarshalJSON() + if err == nil { + // copy JSON into buffer, checking validity. + err = compact(&e.Buffer, b, true) + } + if err != nil { + e.error(&MarshalerError{v.Type(), err}) + } +} + +func addrMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { + va := v.Addr() + if va.IsNil() { + e.WriteString("null") return } + m := va.Interface().(Marshaler) + b, err := m.MarshalJSON() + if err == nil { + // copy JSON into buffer, checking validity. + err = compact(&e.Buffer, b, true) + } + if err != nil { + e.error(&MarshalerError{v.Type(), err}) + } +} - writeString := (*encodeState).WriteString +func textMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { + if v.Kind() == reflect.Ptr && v.IsNil() { + e.WriteString("null") + return + } + m := v.Interface().(encoding.TextMarshaler) + b, err := m.MarshalText() + if err == nil { + _, err = e.stringBytes(b) + } + if err != nil { + e.error(&MarshalerError{v.Type(), err}) + } +} + +func addrTextMarshalerEncoder(e *encodeState, v reflect.Value, quoted bool) { + va := v.Addr() + if va.IsNil() { + e.WriteString("null") + return + } + m := va.Interface().(encoding.TextMarshaler) + b, err := m.MarshalText() + if err == nil { + _, err = e.stringBytes(b) + } + if err != nil { + e.error(&MarshalerError{v.Type(), err}) + } +} + +func boolEncoder(e *encodeState, v reflect.Value, quoted bool) { + if quoted { + e.WriteByte('"') + } + if v.Bool() { + e.WriteString("true") + } else { + e.WriteString("false") + } + if quoted { + e.WriteByte('"') + } +} + +func intEncoder(e *encodeState, v reflect.Value, quoted bool) { + b := strconv.AppendInt(e.scratch[:0], v.Int(), 10) + if quoted { + e.WriteByte('"') + } + e.Write(b) if quoted { - writeString = (*encodeState).string + e.WriteByte('"') } +} - switch v.Kind() { - case reflect.Bool: - x := v.Bool() - if x { - writeString(e, "true") - } else { - writeString(e, "false") - } +func uintEncoder(e *encodeState, v reflect.Value, quoted bool) { + b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10) + if quoted { + e.WriteByte('"') + } + e.Write(b) + if quoted { + e.WriteByte('"') + } +} - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - b := strconv.AppendInt(e.scratch[:0], v.Int(), 10) - if quoted { - writeString(e, string(b)) - } else { - e.Write(b) +type floatEncoder int // number of bits + +func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) { + f := v.Float() + if math.IsInf(f, 0) || math.IsNaN(f) { + e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))}) + } + b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits)) + if quoted { + e.WriteByte('"') + } + e.Write(b) + if quoted { + e.WriteByte('"') + } +} + +var ( + float32Encoder = (floatEncoder(32)).encode + float64Encoder = (floatEncoder(64)).encode +) + +func stringEncoder(e *encodeState, v reflect.Value, quoted bool) { + if v.Type() == numberType { + numStr := v.String() + if numStr == "" { + numStr = "0" // Number's zero-val } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10) - if quoted { - writeString(e, string(b)) - } else { - e.Write(b) + e.WriteString(numStr) + return + } + if quoted { + sb, err := Marshal(v.String()) + if err != nil { + e.error(err) } - case reflect.Float32, reflect.Float64: - f := v.Float() - if math.IsInf(f, 0) || math.IsNaN(f) { - e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, v.Type().Bits())}) + e.string(string(sb)) + } else { + e.string(v.String()) + } +} + +func interfaceEncoder(e *encodeState, v reflect.Value, quoted bool) { + if v.IsNil() { + e.WriteString("null") + return + } + e.reflectValue(v.Elem()) +} + +func unsupportedTypeEncoder(e *encodeState, v reflect.Value, quoted bool) { + e.error(&UnsupportedTypeError{v.Type()}) +} + +type structEncoder struct { + fields []field + fieldEncs []encoderFunc +} + +func (se *structEncoder) encode(e *encodeState, v reflect.Value, quoted bool) { + e.WriteByte('{') + first := true + for i, f := range se.fields { + fv := fieldByIndex(v, f.index) + if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { + continue } - b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, v.Type().Bits()) - if quoted { - writeString(e, string(b)) + if first { + first = false } else { - e.Write(b) - } - case reflect.String: - if v.Type() == numberType { - numStr := v.String() - if numStr == "" { - numStr = "0" // Number's zero-val - } - e.WriteString(numStr) - break - } - if quoted { - sb, err := Marshal(v.String()) - if err != nil { - e.error(err) - } - e.string(string(sb)) - } else { - e.string(v.String()) + e.WriteByte(',') } + e.string(f.name) + e.WriteByte(':') + se.fieldEncs[i](e, fv, f.quoted) + } + e.WriteByte('}') +} - case reflect.Struct: - e.WriteByte('{') - first := true - for _, f := range cachedTypeFields(v.Type()) { - fv := fieldByIndex(v, f.index) - if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { - continue - } - if first { - first = false - } else { - e.WriteByte(',') - } - e.string(f.name) - e.WriteByte(':') - e.reflectValueQuoted(fv, f.quoted) - } - e.WriteByte('}') +func newStructEncoder(t reflect.Type) encoderFunc { + fields := cachedTypeFields(t) + se := &structEncoder{ + fields: fields, + fieldEncs: make([]encoderFunc, len(fields)), + } + for i, f := range fields { + se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index)) + } + return se.encode +} - case reflect.Map: - if v.Type().Key().Kind() != reflect.String { - e.error(&UnsupportedTypeError{v.Type()}) - } - if v.IsNil() { - e.WriteString("null") - break - } - e.WriteByte('{') - var sv stringValues = v.MapKeys() - sort.Sort(sv) - for i, k := range sv { - if i > 0 { - e.WriteByte(',') - } - e.string(k.String()) - e.WriteByte(':') - e.reflectValue(v.MapIndex(k)) - } - e.WriteByte('}') +type mapEncoder struct { + elemEnc encoderFunc +} - case reflect.Slice: - if v.IsNil() { - e.WriteString("null") - break - } - if v.Type().Elem().Kind() == reflect.Uint8 { - // Byte slices get special treatment; arrays don't. - s := v.Bytes() - e.WriteByte('"') - if len(s) < 1024 { - // for small buffers, using Encode directly is much faster. - dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) - base64.StdEncoding.Encode(dst, s) - e.Write(dst) - } else { - // for large buffers, avoid unnecessary extra temporary - // buffer space. - enc := base64.NewEncoder(base64.StdEncoding, e) - enc.Write(s) - enc.Close() - } - e.WriteByte('"') - break - } - // Slices can be marshalled as nil, but otherwise are handled - // as arrays. - fallthrough - case reflect.Array: - e.WriteByte('[') - n := v.Len() - for i := 0; i < n; i++ { - if i > 0 { - e.WriteByte(',') - } - e.reflectValue(v.Index(i)) +func (me *mapEncoder) encode(e *encodeState, v reflect.Value, _ bool) { + if v.IsNil() { + e.WriteString("null") + return + } + e.WriteByte('{') + var sv stringValues = v.MapKeys() + sort.Sort(sv) + for i, k := range sv { + if i > 0 { + e.WriteByte(',') } - e.WriteByte(']') + e.string(k.String()) + e.WriteByte(':') + me.elemEnc(e, v.MapIndex(k), false) + } + e.WriteByte('}') +} - case reflect.Interface, reflect.Ptr: - if v.IsNil() { - e.WriteString("null") - return +func newMapEncoder(t reflect.Type) encoderFunc { + if t.Key().Kind() != reflect.String { + return unsupportedTypeEncoder + } + me := &mapEncoder{typeEncoder(t.Elem())} + return me.encode +} + +func encodeByteSlice(e *encodeState, v reflect.Value, _ bool) { + if v.IsNil() { + e.WriteString("null") + return + } + s := v.Bytes() + e.WriteByte('"') + if len(s) < 1024 { + // for small buffers, using Encode directly is much faster. + dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) + base64.StdEncoding.Encode(dst, s) + e.Write(dst) + } else { + // for large buffers, avoid unnecessary extra temporary + // buffer space. + enc := base64.NewEncoder(base64.StdEncoding, e) + enc.Write(s) + enc.Close() + } + e.WriteByte('"') +} + +// sliceEncoder just wraps an arrayEncoder, checking to make sure the value isn't nil. +type sliceEncoder struct { + arrayEnc encoderFunc +} + +func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, _ bool) { + if v.IsNil() { + e.WriteString("null") + return + } + se.arrayEnc(e, v, false) +} + +func newSliceEncoder(t reflect.Type) encoderFunc { + // Byte slices get special treatment; arrays don't. + if t.Elem().Kind() == reflect.Uint8 { + return encodeByteSlice + } + enc := &sliceEncoder{newArrayEncoder(t)} + return enc.encode +} + +type arrayEncoder struct { + elemEnc encoderFunc +} + +func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, _ bool) { + e.WriteByte('[') + n := v.Len() + for i := 0; i < n; i++ { + if i > 0 { + e.WriteByte(',') } - e.reflectValue(v.Elem()) + ae.elemEnc(e, v.Index(i), false) + } + e.WriteByte(']') +} - default: - e.error(&UnsupportedTypeError{v.Type()}) +func newArrayEncoder(t reflect.Type) encoderFunc { + enc := &arrayEncoder{typeEncoder(t.Elem())} + return enc.encode +} + +type ptrEncoder struct { + elemEnc encoderFunc +} + +func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, _ bool) { + if v.IsNil() { + e.WriteString("null") + return + } + pe.elemEnc(e, v.Elem(), false) +} + +func newPtrEncoder(t reflect.Type) encoderFunc { + enc := &ptrEncoder{typeEncoder(t.Elem())} + return enc.encode +} + +type condAddrEncoder struct { + canAddrEnc, elseEnc encoderFunc +} + +func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, quoted bool) { + if v.CanAddr() { + ce.canAddrEnc(e, v, quoted) + } else { + ce.elseEnc(e, v, quoted) } - return +} + +// newCondAddrEncoder returns an encoder that checks whether its value +// CanAddr and delegates to canAddrEnc if so, else to elseEnc. +func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc { + enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} + return enc.encode } func isValidTag(s string) bool { @@ -479,6 +768,16 @@ func fieldByIndex(v reflect.Value, index []int) reflect.Value { return v } +func typeByIndex(t reflect.Type, index []int) reflect.Type { + for _, i := range index { + if t.Kind() == reflect.Ptr { + t = t.Elem() + } + t = t.Field(i).Type + } + return t +} + // stringValues is a slice of reflect.Value holding *reflect.StringValue. // It implements the methods to sort by string. type stringValues []reflect.Value @@ -488,13 +787,14 @@ func (sv stringValues) Swap(i, j int) { sv[i], sv[j] = sv[j], sv[i] } func (sv stringValues) Less(i, j int) bool { return sv.get(i) < sv.get(j) } func (sv stringValues) get(i int) string { return sv[i].String() } +// NOTE: keep in sync with stringBytes below. func (e *encodeState) string(s string) (int, error) { len0 := e.Len() e.WriteByte('"') start := 0 for i := 0; i < len(s); { if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' { + if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { i++ continue } @@ -526,7 +826,30 @@ func (e *encodeState) string(s string) (int, error) { } c, size := utf8.DecodeRuneInString(s[i:]) if c == utf8.RuneError && size == 1 { - e.error(&InvalidUTF8Error{s}) + if start < i { + e.WriteString(s[start:i]) + } + e.WriteString(`\ufffd`) + i += size + start = i + continue + } + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + if c == '\u2028' || c == '\u2029' { + if start < i { + e.WriteString(s[start:i]) + } + e.WriteString(`\u202`) + e.WriteByte(hex[c&0xF]) + i += size + start = i + continue } i += size } @@ -537,6 +860,79 @@ func (e *encodeState) string(s string) (int, error) { return e.Len() - len0, nil } +// NOTE: keep in sync with string above. +func (e *encodeState) stringBytes(s []byte) (int, error) { + len0 := e.Len() + e.WriteByte('"') + start := 0 + for i := 0; i < len(s); { + if b := s[i]; b < utf8.RuneSelf { + if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { + i++ + continue + } + if start < i { + e.Write(s[start:i]) + } + switch b { + case '\\', '"': + e.WriteByte('\\') + e.WriteByte(b) + case '\n': + e.WriteByte('\\') + e.WriteByte('n') + case '\r': + e.WriteByte('\\') + e.WriteByte('r') + default: + // This encodes bytes < 0x20 except for \n and \r, + // as well as < and >. The latter are escaped because they + // can lead to security holes when user-controlled strings + // are rendered into JSON and served to some browsers. + e.WriteString(`\u00`) + e.WriteByte(hex[b>>4]) + e.WriteByte(hex[b&0xF]) + } + i++ + start = i + continue + } + c, size := utf8.DecodeRune(s[i:]) + if c == utf8.RuneError && size == 1 { + if start < i { + e.Write(s[start:i]) + } + e.WriteString(`\ufffd`) + i += size + start = i + continue + } + // U+2028 is LINE SEPARATOR. + // U+2029 is PARAGRAPH SEPARATOR. + // They are both technically valid characters in JSON strings, + // but don't work in JSONP, which has to be evaluated as JavaScript, + // and can lead to security holes there. It is valid JSON to + // escape them, so we do so unconditionally. + // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. + if c == '\u2028' || c == '\u2029' { + if start < i { + e.Write(s[start:i]) + } + e.WriteString(`\u202`) + e.WriteByte(hex[c&0xF]) + i += size + start = i + continue + } + i += size + } + if start < len(s) { + e.Write(s[start:]) + } + e.WriteByte('"') + return e.Len() - len0, nil +} + // A field represents a single field found in a struct. type field struct { name string diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go index 5be0a99..9395db7 100644 --- a/libgo/go/encoding/json/encode_test.go +++ b/libgo/go/encoding/json/encode_test.go @@ -9,6 +9,7 @@ import ( "math" "reflect" "testing" + "unicode" ) type Optionals struct { @@ -146,19 +147,46 @@ func (Val) MarshalJSON() ([]byte, error) { return []byte(`"val"`), nil } +// RefText has Marshaler and Unmarshaler methods with pointer receiver. +type RefText int + +func (*RefText) MarshalText() ([]byte, error) { + return []byte(`"ref"`), nil +} + +func (r *RefText) UnmarshalText([]byte) error { + *r = 13 + return nil +} + +// ValText has Marshaler methods with value receiver. +type ValText int + +func (ValText) MarshalText() ([]byte, error) { + return []byte(`"val"`), nil +} + func TestRefValMarshal(t *testing.T) { var s = struct { R0 Ref R1 *Ref + R2 RefText + R3 *RefText V0 Val V1 *Val + V2 ValText + V3 *ValText }{ R0: 12, R1: new(Ref), + R2: 14, + R3: new(RefText), V0: 13, V1: new(Val), + V2: 15, + V3: new(ValText), } - const want = `{"R0":"ref","R1":"ref","V0":"val","V1":"val"}` + const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` b, err := Marshal(&s) if err != nil { t.Fatalf("Marshal: %v", err) @@ -175,15 +203,32 @@ func (C) MarshalJSON() ([]byte, error) { return []byte(`"<&>"`), nil } +// CText implements Marshaler and returns unescaped text. +type CText int + +func (CText) MarshalText() ([]byte, error) { + return []byte(`"<&>"`), nil +} + func TestMarshalerEscaping(t *testing.T) { var c C - const want = `"\u003c\u0026\u003e"` + want := `"\u003c\u0026\u003e"` b, err := Marshal(c) if err != nil { - t.Fatalf("Marshal: %v", err) + t.Fatalf("Marshal(c): %v", err) } if got := string(b); got != want { - t.Errorf("got %q, want %q", got, want) + t.Errorf("Marshal(c) = %#q, want %#q", got, want) + } + + var ct CText + want = `"\"\u003c\u0026\u003e\""` + b, err = Marshal(ct) + if err != nil { + t.Fatalf("Marshal(ct): %v", err) + } + if got := string(b); got != want { + t.Errorf("Marshal(ct) = %#q, want %#q", got, want) } } @@ -310,3 +355,73 @@ func TestDuplicatedFieldDisappears(t *testing.T) { t.Fatalf("Marshal: got %s want %s", got, want) } } + +func TestStringBytes(t *testing.T) { + // Test that encodeState.stringBytes and encodeState.string use the same encoding. + es := &encodeState{} + var r []rune + for i := '\u0000'; i <= unicode.MaxRune; i++ { + r = append(r, i) + } + s := string(r) + "\xff\xff\xffhello" // some invalid UTF-8 too + _, err := es.string(s) + if err != nil { + t.Fatal(err) + } + + esBytes := &encodeState{} + _, err = esBytes.stringBytes([]byte(s)) + if err != nil { + t.Fatal(err) + } + + enc := es.Buffer.String() + encBytes := esBytes.Buffer.String() + if enc != encBytes { + i := 0 + for i < len(enc) && i < len(encBytes) && enc[i] == encBytes[i] { + i++ + } + enc = enc[i:] + encBytes = encBytes[i:] + i = 0 + for i < len(enc) && i < len(encBytes) && enc[len(enc)-i-1] == encBytes[len(encBytes)-i-1] { + i++ + } + enc = enc[:len(enc)-i] + encBytes = encBytes[:len(encBytes)-i] + + if len(enc) > 20 { + enc = enc[:20] + "..." + } + if len(encBytes) > 20 { + encBytes = encBytes[:20] + "..." + } + + t.Errorf("encodings differ at %#q vs %#q", enc, encBytes) + } +} + +func TestIssue6458(t *testing.T) { + type Foo struct { + M RawMessage + } + x := Foo{RawMessage(`"foo"`)} + + b, err := Marshal(&x) + if err != nil { + t.Fatal(err) + } + if want := `{"M":"foo"}`; string(b) != want { + t.Errorf("Marshal(&x) = %#q; want %#q", b, want) + } + + b, err = Marshal(x) + if err != nil { + t.Fatal(err) + } + + if want := `{"M":"ImZvbyI="}`; string(b) != want { + t.Errorf("Marshal(x) = %#q; want %#q", b, want) + } +} diff --git a/libgo/go/encoding/json/indent.go b/libgo/go/encoding/json/indent.go index e8dfa4e..11ef709 100644 --- a/libgo/go/encoding/json/indent.go +++ b/libgo/go/encoding/json/indent.go @@ -27,6 +27,15 @@ func compact(dst *bytes.Buffer, src []byte, escape bool) error { dst.WriteByte(hex[c&0xF]) start = i + 1 } + // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9). + if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 { + if start < i { + dst.Write(src[start:i]) + } + dst.WriteString(`\u202`) + dst.WriteByte(hex[src[i+2]&0xF]) + start = i + 3 + } v := scan.step(&scan, int(c)) if v >= scanSkipSpace { if v == scanError { diff --git a/libgo/go/encoding/json/scanner.go b/libgo/go/encoding/json/scanner.go index 054b6b3..a4609c8 100644 --- a/libgo/go/encoding/json/scanner.go +++ b/libgo/go/encoding/json/scanner.go @@ -390,7 +390,7 @@ func stateInStringEscU123(s *scanner, c int) int { return s.error(c, "in \\u hexadecimal character escape") } -// stateInStringEscU123 is the state after reading `-` during a number. +// stateNeg is the state after reading `-` during a number. func stateNeg(s *scanner, c int) int { if c == '0' { s.step = state0 diff --git a/libgo/go/encoding/json/scanner_test.go b/libgo/go/encoding/json/scanner_test.go index 77d3455..90e45ff 100644 --- a/libgo/go/encoding/json/scanner_test.go +++ b/libgo/go/encoding/json/scanner_test.go @@ -63,6 +63,25 @@ func TestCompact(t *testing.T) { } } +func TestCompactSeparators(t *testing.T) { + // U+2028 and U+2029 should be escaped inside strings. + // They should not appear outside strings. + tests := []struct { + in, compact string + }{ + {"{\"\u2028\": 1}", `{"\u2028":1}`}, + {"{\"\u2029\" :2}", `{"\u2029":2}`}, + } + for _, tt := range tests { + var buf bytes.Buffer + if err := Compact(&buf, []byte(tt.in)); err != nil { + t.Errorf("Compact(%q): %v", tt.in, err) + } else if s := buf.String(); s != tt.compact { + t.Errorf("Compact(%q) = %q, want %q", tt.in, s, tt.compact) + } + } +} + func TestIndent(t *testing.T) { var buf bytes.Buffer for _, tt := range examples { diff --git a/libgo/go/encoding/json/stream.go b/libgo/go/encoding/json/stream.go index 00f4726..1928aba 100644 --- a/libgo/go/encoding/json/stream.go +++ b/libgo/go/encoding/json/stream.go @@ -148,7 +148,7 @@ func NewEncoder(w io.Writer) *Encoder { return &Encoder{w: w} } -// Encode writes the JSON encoding of v to the connection. +// Encode writes the JSON encoding of v to the stream. // // See the documentation for Marshal for details about the // conversion of Go values to JSON. @@ -156,8 +156,8 @@ func (enc *Encoder) Encode(v interface{}) error { if enc.err != nil { return enc.err } - enc.e.Reset() - err := enc.e.marshal(v) + e := newEncodeState() + err := e.marshal(v) if err != nil { return err } @@ -168,11 +168,12 @@ func (enc *Encoder) Encode(v interface{}) error { // is required if the encoded value was a number, // so that the reader knows there aren't more // digits coming. - enc.e.WriteByte('\n') + e.WriteByte('\n') - if _, err = enc.w.Write(enc.e.Bytes()); err != nil { + if _, err = enc.w.Write(e.Bytes()); err != nil { enc.err = err } + putEncodeState(e) return err } diff --git a/libgo/go/encoding/json/stream_test.go b/libgo/go/encoding/json/stream_test.go index 07c9e1d..b562e87 100644 --- a/libgo/go/encoding/json/stream_test.go +++ b/libgo/go/encoding/json/stream_test.go @@ -191,3 +191,16 @@ func TestBlocking(t *testing.T) { w.Close() } } + +func BenchmarkEncoderEncode(b *testing.B) { + b.ReportAllocs() + type T struct { + X, Y string + } + v := &T{"foo", "bar"} + for i := 0; i < b.N; i++ { + if err := NewEncoder(ioutil.Discard).Encode(v); err != nil { + b.Fatal(err) + } + } +} diff --git a/libgo/go/encoding/json/tags.go b/libgo/go/encoding/json/tags.go index 58cda20..c38fd51 100644 --- a/libgo/go/encoding/json/tags.go +++ b/libgo/go/encoding/json/tags.go @@ -21,7 +21,7 @@ func parseTag(tag string) (string, tagOptions) { return tag, tagOptions("") } -// Contains returns whether checks that a comma-separated list of options +// Contains reports whether a comma-separated list of options // contains a particular substr flag. substr must be surrounded by a // string boundary or commas. func (o tagOptions) Contains(optionName string) bool { diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go index 47b0017..d9522e0 100644 --- a/libgo/go/encoding/xml/marshal.go +++ b/libgo/go/encoding/xml/marshal.go @@ -7,12 +7,12 @@ package xml import ( "bufio" "bytes" + "encoding" "fmt" "io" "reflect" "strconv" "strings" - "time" ) const ( @@ -75,6 +75,41 @@ func Marshal(v interface{}) ([]byte, error) { return b.Bytes(), nil } +// Marshaler is the interface implemented by objects that can marshal +// themselves into valid XML elements. +// +// MarshalXML encodes the receiver as zero or more XML elements. +// By convention, arrays or slices are typically encoded as a sequence +// of elements, one per entry. +// Using start as the element tag is not required, but doing so +// will enable Unmarshal to match the XML elements to the correct +// struct field. +// One common implementation strategy is to construct a separate +// value with a layout corresponding to the desired XML and then +// to encode it using e.EncodeElement. +// Another common strategy is to use repeated calls to e.EncodeToken +// to generate the XML output one token at a time. +// The sequence of encoded tokens must make up zero or more valid +// XML elements. +type Marshaler interface { + MarshalXML(e *Encoder, start StartElement) error +} + +// MarshalerAttr is the interface implemented by objects that can marshal +// themselves into valid XML attributes. +// +// MarshalXMLAttr returns an XML attribute with the encoded value of the receiver. +// Using name as the attribute name is not required, but doing so +// will enable Unmarshal to match the attribute to the correct +// struct field. +// If MarshalXMLAttr returns the zero attribute Attr{}, no attribute +// will be generated in the output. +// MarshalXMLAttr is used only for struct fields with the +// "attr" option in the field tag. +type MarshalerAttr interface { + MarshalXMLAttr(name Name) (Attr, error) +} + // MarshalIndent works like Marshal, but each XML element begins on a new // indented line that starts with prefix and is followed by one or more // copies of indent according to the nesting depth. @@ -90,36 +125,124 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { // An Encoder writes XML data to an output stream. type Encoder struct { - printer + p printer } // NewEncoder returns a new encoder that writes to w. func NewEncoder(w io.Writer) *Encoder { - return &Encoder{printer{Writer: bufio.NewWriter(w)}} + e := &Encoder{printer{Writer: bufio.NewWriter(w)}} + e.p.encoder = e + return e } // Indent sets the encoder to generate XML in which each element // begins on a new indented line that starts with prefix and is followed by // one or more copies of indent according to the nesting depth. func (enc *Encoder) Indent(prefix, indent string) { - enc.prefix = prefix - enc.indent = indent + enc.p.prefix = prefix + enc.p.indent = indent } // Encode writes the XML encoding of v to the stream. // // See the documentation for Marshal for details about the conversion // of Go values to XML. +// +// Encode calls Flush before returning. func (enc *Encoder) Encode(v interface{}) error { - err := enc.marshalValue(reflect.ValueOf(v), nil) + err := enc.p.marshalValue(reflect.ValueOf(v), nil, nil) if err != nil { return err } - return enc.Flush() + return enc.p.Flush() +} + +// EncodeElement writes the XML encoding of v to the stream, +// using start as the outermost tag in the encoding. +// +// See the documentation for Marshal for details about the conversion +// of Go values to XML. +// +// EncodeElement calls Flush before returning. +func (enc *Encoder) EncodeElement(v interface{}, start StartElement) error { + err := enc.p.marshalValue(reflect.ValueOf(v), nil, &start) + if err != nil { + return err + } + return enc.p.Flush() +} + +var ( + endComment = []byte("-->") + endProcInst = []byte("?>") + endDirective = []byte(">") +) + +// EncodeToken writes the given XML token to the stream. +// It returns an error if StartElement and EndElement tokens are not properly matched. +// +// 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. +func (enc *Encoder) EncodeToken(t Token) error { + p := &enc.p + switch t := t.(type) { + case StartElement: + if err := p.writeStart(&t); err != nil { + return err + } + case EndElement: + if err := p.writeEnd(t.Name); err != nil { + return err + } + case CharData: + EscapeText(p, t) + case Comment: + if bytes.Contains(t, endComment) { + return fmt.Errorf("xml: EncodeToken of Comment containing --> marker") + } + p.WriteString("<!--") + p.Write(t) + p.WriteString("-->") + return p.cachedWriteError() + case ProcInst: + if t.Target == "xml" || !isNameString(t.Target) { + return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target") + } + if bytes.Contains(t.Inst, endProcInst) { + return fmt.Errorf("xml: EncodeToken of ProcInst containing ?> marker") + } + p.WriteString("<?") + p.WriteString(t.Target) + if len(t.Inst) > 0 { + p.WriteByte(' ') + p.Write(t.Inst) + } + p.WriteString("?>") + case Directive: + if bytes.Contains(t, endDirective) { + return fmt.Errorf("xml: EncodeToken of Directive containing > marker") + } + p.WriteString("<!") + p.Write(t) + p.WriteString(">") + } + return p.cachedWriteError() +} + +// Flush flushes any buffered XML to the underlying writer. +// See the EncodeToken documentation for details about when it is necessary. +func (enc *Encoder) Flush() error { + return enc.p.Flush() } type printer struct { *bufio.Writer + encoder *Encoder seq int indent string prefix string @@ -128,13 +251,15 @@ type printer struct { putNewline bool attrNS map[string]string // map prefix -> name space attrPrefix map[string]string // map name space -> prefix + prefixes []string + tags []Name } // createAttrPrefix finds the name space prefix attribute to use for the given name space, -// defining a new prefix if necessary. It returns the prefix and whether it is new. -func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) { - if prefix = p.attrPrefix[url]; prefix != "" { - return prefix, false +// defining a new prefix if necessary. It returns the prefix. +func (p *printer) createAttrPrefix(url string) string { + if prefix := p.attrPrefix[url]; prefix != "" { + return prefix } // The "http://www.w3.org/XML/1998/namespace" name space is predefined as "xml" @@ -142,7 +267,7 @@ func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) { // (The "http://www.w3.org/2000/xmlns/" name space is also predefined as "xmlns", // but users should not be trying to use that one directly - that's our job.) if url == xmlURL { - return "xml", false + return "xml" } // Need to define a new name space. @@ -153,7 +278,7 @@ func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) { // Pick a name. We try to use the final element of the path // but fall back to _. - prefix = strings.TrimRight(url, "/") + prefix := strings.TrimRight(url, "/") if i := strings.LastIndex(prefix, "/"); i >= 0 { prefix = prefix[i+1:] } @@ -183,7 +308,9 @@ func (p *printer) createAttrPrefix(url string) (prefix string, isNew bool) { EscapeText(p, []byte(url)) p.WriteString(`" `) - return prefix, true + p.prefixes = append(p.prefixes, prefix) + + return prefix } // deleteAttrPrefix removes an attribute name space prefix. @@ -192,9 +319,34 @@ func (p *printer) deleteAttrPrefix(prefix string) { delete(p.attrNS, prefix) } +func (p *printer) markPrefix() { + p.prefixes = append(p.prefixes, "") +} + +func (p *printer) popPrefix() { + for len(p.prefixes) > 0 { + prefix := p.prefixes[len(p.prefixes)-1] + p.prefixes = p.prefixes[:len(p.prefixes)-1] + if prefix == "" { + break + } + p.deleteAttrPrefix(prefix) + } +} + +var ( + marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() + marshalerAttrType = reflect.TypeOf((*MarshalerAttr)(nil)).Elem() + textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() +) + // marshalValue writes one or more XML elements representing val. // If val was obtained from a struct field, finfo must have its details. -func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { +func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo, startTemplate *StartElement) error { + if startTemplate != nil && startTemplate.Name.Local == "" { + return fmt.Errorf("xml: EncodeElement of StartElement with missing name") + } + if !val.IsValid() { return nil } @@ -202,21 +354,45 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { return nil } + // Drill into interfaces and pointers. + // This can turn into an infinite loop given a cyclic chain, + // but it matches the Go 1 behavior. + for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { + if val.IsNil() { + return nil + } + val = val.Elem() + } + kind := val.Kind() typ := val.Type() - // Drill into pointers/interfaces - if kind == reflect.Ptr || kind == reflect.Interface { - if val.IsNil() { - return nil + // Check for marshaler. + if val.CanInterface() && typ.Implements(marshalerType) { + return p.marshalInterface(val.Interface().(Marshaler), defaultStart(typ, finfo, startTemplate)) + } + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() && pv.Type().Implements(marshalerType) { + return p.marshalInterface(pv.Interface().(Marshaler), defaultStart(pv.Type(), finfo, startTemplate)) + } + } + + // Check for text marshaler. + if val.CanInterface() && typ.Implements(textMarshalerType) { + return p.marshalTextInterface(val.Interface().(encoding.TextMarshaler), defaultStart(typ, finfo, startTemplate)) + } + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { + return p.marshalTextInterface(pv.Interface().(encoding.TextMarshaler), defaultStart(pv.Type(), finfo, startTemplate)) } - return p.marshalValue(val.Elem(), finfo) } // Slices and arrays iterate over the elements. They do not have an enclosing tag. if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 { for i, n := 0, val.Len(); i < n; i++ { - if err := p.marshalValue(val.Index(i), finfo); err != nil { + if err := p.marshalValue(val.Index(i), finfo, startTemplate); err != nil { return err } } @@ -228,40 +404,34 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { return err } + // Create start element. // Precedence for the XML element name is: + // 0. startTemplate // 1. XMLName field in underlying struct; // 2. field name/tag in the struct field; and // 3. type name - var xmlns, name string - if tinfo.xmlname != nil { + var start StartElement + + if startTemplate != nil { + start.Name = startTemplate.Name + start.Attr = append(start.Attr, startTemplate.Attr...) + } else if tinfo.xmlname != nil { xmlname := tinfo.xmlname if xmlname.name != "" { - xmlns, name = xmlname.xmlns, xmlname.name + start.Name.Space, start.Name.Local = xmlname.xmlns, xmlname.name } else if v, ok := xmlname.value(val).Interface().(Name); ok && v.Local != "" { - xmlns, name = v.Space, v.Local + start.Name = v } } - if name == "" && finfo != nil { - xmlns, name = finfo.xmlns, finfo.name + if start.Name.Local == "" && finfo != nil { + start.Name.Space, start.Name.Local = finfo.xmlns, finfo.name } - if name == "" { - name = typ.Name() + if start.Name.Local == "" { + name := typ.Name() if name == "" { return &UnsupportedTypeError{typ} } - } - - p.writeIndent(1) - p.WriteByte('<') - p.WriteString(name) - - if xmlns != "" { - p.WriteString(` xmlns="`) - // TODO: EscapeString, to avoid the allocation. - if err := EscapeText(p, []byte(xmlns)); err != nil { - return err - } - p.WriteByte('"') + start.Name.Local = name } // Attributes @@ -271,67 +441,243 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error { continue } fv := finfo.value(val) + name := Name{Space: finfo.xmlns, Local: finfo.name} + if finfo.flags&fOmitEmpty != 0 && isEmptyValue(fv) { continue } - p.WriteByte(' ') - if finfo.xmlns != "" { - prefix, created := p.createAttrPrefix(finfo.xmlns) - if created { - defer p.deleteAttrPrefix(prefix) + + if fv.Kind() == reflect.Interface && fv.IsNil() { + continue + } + + if fv.CanInterface() && fv.Type().Implements(marshalerAttrType) { + attr, err := fv.Interface().(MarshalerAttr).MarshalXMLAttr(name) + if err != nil { + return err } - p.WriteString(prefix) - p.WriteByte(':') + if attr.Name.Local != "" { + start.Attr = append(start.Attr, attr) + } + continue } - p.WriteString(finfo.name) - p.WriteString(`="`) - if err := p.marshalSimple(fv.Type(), fv); err != nil { + + if fv.CanAddr() { + pv := fv.Addr() + if pv.CanInterface() && pv.Type().Implements(marshalerAttrType) { + attr, err := pv.Interface().(MarshalerAttr).MarshalXMLAttr(name) + if err != nil { + return err + } + if attr.Name.Local != "" { + start.Attr = append(start.Attr, attr) + } + continue + } + } + + if fv.CanInterface() && fv.Type().Implements(textMarshalerType) { + text, err := fv.Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return err + } + start.Attr = append(start.Attr, Attr{name, string(text)}) + continue + } + + if fv.CanAddr() { + pv := fv.Addr() + if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { + text, err := pv.Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return err + } + start.Attr = append(start.Attr, Attr{name, string(text)}) + continue + } + } + + // Dereference or skip nil pointer, interface values. + switch fv.Kind() { + case reflect.Ptr, reflect.Interface: + if fv.IsNil() { + continue + } + fv = fv.Elem() + } + + s, b, err := p.marshalSimple(fv.Type(), fv) + if err != nil { return err } - p.WriteByte('"') + if b != nil { + s = string(b) + } + start.Attr = append(start.Attr, Attr{name, s}) + } + + if err := p.writeStart(&start); err != nil { + return err } - p.WriteByte('>') if val.Kind() == reflect.Struct { err = p.marshalStruct(tinfo, val) } else { - err = p.marshalSimple(typ, val) + s, b, err1 := p.marshalSimple(typ, val) + if err1 != nil { + err = err1 + } else if b != nil { + EscapeText(p, b) + } else { + p.EscapeString(s) + } } if err != nil { return err } - p.writeIndent(-1) - p.WriteByte('<') - p.WriteByte('/') - p.WriteString(name) - p.WriteByte('>') + if err := p.writeEnd(start.Name); err != nil { + return err + } return p.cachedWriteError() } -var timeType = reflect.TypeOf(time.Time{}) +// defaultStart returns the default start element to use, +// given the reflect type, field info, and start template. +func defaultStart(typ reflect.Type, finfo *fieldInfo, startTemplate *StartElement) StartElement { + var start StartElement + // Precedence for the XML element name is as above, + // except that we do not look inside structs for the first field. + if startTemplate != nil { + start.Name = startTemplate.Name + start.Attr = append(start.Attr, startTemplate.Attr...) + } else if finfo != nil && finfo.name != "" { + start.Name.Local = finfo.name + start.Name.Space = finfo.xmlns + } else if typ.Name() != "" { + start.Name.Local = typ.Name() + } else { + // Must be a pointer to a named type, + // since it has the Marshaler methods. + start.Name.Local = typ.Elem().Name() + } + return start +} -func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error { - // Normally we don't see structs, but this can happen for an attribute. - if val.Type() == timeType { - p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano)) - return nil +// marshalInterface marshals a Marshaler interface value. +func (p *printer) marshalInterface(val Marshaler, start StartElement) error { + // Push a marker onto the tag stack so that MarshalXML + // cannot close the XML tags that it did not open. + p.tags = append(p.tags, Name{}) + n := len(p.tags) + + err := val.MarshalXML(p.encoder, start) + if err != nil { + return err + } + + // Make sure MarshalXML closed all its tags. p.tags[n-1] is the mark. + if len(p.tags) > n { + return fmt.Errorf("xml: %s.MarshalXML wrote invalid XML: <%s> not closed", receiverType(val), p.tags[len(p.tags)-1].Local) + } + p.tags = p.tags[:n-1] + return nil +} + +// marshalTextInterface marshals a TextMarshaler interface value. +func (p *printer) marshalTextInterface(val encoding.TextMarshaler, start StartElement) error { + if err := p.writeStart(&start); err != nil { + return err + } + text, err := val.MarshalText() + if err != nil { + return err + } + EscapeText(p, text) + return p.writeEnd(start.Name) +} + +// writeStart writes the given start element. +func (p *printer) writeStart(start *StartElement) error { + if start.Name.Local == "" { + return fmt.Errorf("xml: start tag with no name") + } + + p.tags = append(p.tags, start.Name) + p.markPrefix() + + p.writeIndent(1) + p.WriteByte('<') + p.WriteString(start.Name.Local) + + if start.Name.Space != "" { + p.WriteString(` xmlns="`) + p.EscapeString(start.Name.Space) + p.WriteByte('"') + } + + // Attributes + for _, attr := range start.Attr { + name := attr.Name + if name.Local == "" { + continue + } + p.WriteByte(' ') + if name.Space != "" { + p.WriteString(p.createAttrPrefix(name.Space)) + p.WriteByte(':') + } + p.WriteString(name.Local) + p.WriteString(`="`) + p.EscapeString(attr.Value) + p.WriteByte('"') } + p.WriteByte('>') + return nil +} + +func (p *printer) writeEnd(name Name) error { + if name.Local == "" { + return fmt.Errorf("xml: end tag with no name") + } + if len(p.tags) == 0 || p.tags[len(p.tags)-1].Local == "" { + return fmt.Errorf("xml: end tag </%s> without start tag", name.Local) + } + if top := p.tags[len(p.tags)-1]; top != name { + if top.Local != name.Local { + return fmt.Errorf("xml: end tag </%s> does not match start tag <%s>", name.Local, top.Local) + } + return fmt.Errorf("xml: end tag </%s> in namespace %s does not match start tag <%s> in namespace %s", name.Local, name.Space, top.Local, top.Space) + } + p.tags = p.tags[:len(p.tags)-1] + + p.writeIndent(-1) + p.WriteByte('<') + p.WriteByte('/') + p.WriteString(name.Local) + p.WriteByte('>') + p.popPrefix() + return nil +} + +func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) (string, []byte, error) { switch val.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - p.WriteString(strconv.FormatInt(val.Int(), 10)) + return strconv.FormatInt(val.Int(), 10), nil, nil case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p.WriteString(strconv.FormatUint(val.Uint(), 10)) + return strconv.FormatUint(val.Uint(), 10), nil, nil case reflect.Float32, reflect.Float64: - p.WriteString(strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits())) + return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil, nil case reflect.String: - // TODO: Add EscapeString. - EscapeText(p, []byte(val.String())) + return val.String(), nil, nil case reflect.Bool: - p.WriteString(strconv.FormatBool(val.Bool())) + return strconv.FormatBool(val.Bool()), nil, nil case reflect.Array: - // will be [...]byte + if typ.Elem().Kind() != reflect.Uint8 { + break + } + // [...]byte var bytes []byte if val.CanAddr() { bytes = val.Slice(0, val.Len()).Bytes() @@ -339,32 +685,57 @@ func (p *printer) marshalSimple(typ reflect.Type, val reflect.Value) error { bytes = make([]byte, val.Len()) reflect.Copy(reflect.ValueOf(bytes), val) } - EscapeText(p, bytes) + return "", bytes, nil case reflect.Slice: - // will be []byte - EscapeText(p, val.Bytes()) - default: - return &UnsupportedTypeError{typ} + if typ.Elem().Kind() != reflect.Uint8 { + break + } + // []byte + return "", val.Bytes(), nil } - return p.cachedWriteError() + return "", nil, &UnsupportedTypeError{typ} } var ddBytes = []byte("--") func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { - if val.Type() == timeType { - _, err := p.WriteString(val.Interface().(time.Time).Format(time.RFC3339Nano)) - return err - } - s := parentStack{printer: p} + s := parentStack{p: p} for i := range tinfo.fields { finfo := &tinfo.fields[i] if finfo.flags&fAttr != 0 { continue } vf := finfo.value(val) + + // Dereference or skip nil pointer, interface values. + switch vf.Kind() { + case reflect.Ptr, reflect.Interface: + if !vf.IsNil() { + vf = vf.Elem() + } + } + switch finfo.flags & fMode { case fCharData: + if vf.CanInterface() && vf.Type().Implements(textMarshalerType) { + data, err := vf.Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return err + } + Escape(p, data) + continue + } + if vf.CanAddr() { + pv := vf.Addr() + if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { + data, err := pv.Interface().(encoding.TextMarshaler).MarshalText() + if err != nil { + return err + } + Escape(p, data) + continue + } + } var scratch [64]byte switch vf.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: @@ -385,10 +756,6 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { return err } } - case reflect.Struct: - if vf.Type() == timeType { - Escape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano))) - } } continue @@ -444,14 +811,18 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error { } case fElement, fElement | fAny: - s.trim(finfo.parents) + if err := s.trim(finfo.parents); err != nil { + return err + } if len(finfo.parents) > len(s.stack) { if vf.Kind() != reflect.Ptr && vf.Kind() != reflect.Interface || !vf.IsNil() { - s.push(finfo.parents[len(s.stack):]) + if err := s.push(finfo.parents[len(s.stack):]); err != nil { + return err + } } } } - if err := p.marshalValue(vf, finfo); err != nil { + if err := p.marshalValue(vf, finfo, nil); err != nil { return err } } @@ -497,14 +868,14 @@ func (p *printer) writeIndent(depthDelta int) { } type parentStack struct { - *printer + p *printer stack []string } // trim updates the XML context to match the longest common prefix of the stack // and the given parents. A closing tag will be written for every parent // popped. Passing a zero slice or nil will close all the elements. -func (s *parentStack) trim(parents []string) { +func (s *parentStack) trim(parents []string) error { split := 0 for ; split < len(parents) && split < len(s.stack); split++ { if parents[split] != s.stack[split] { @@ -512,23 +883,23 @@ func (s *parentStack) trim(parents []string) { } } for i := len(s.stack) - 1; i >= split; i-- { - s.writeIndent(-1) - s.WriteString("</") - s.WriteString(s.stack[i]) - s.WriteByte('>') + if err := s.p.writeEnd(Name{Local: s.stack[i]}); err != nil { + return err + } } s.stack = parents[:split] + return nil } // push adds parent elements to the stack and writes open tags. -func (s *parentStack) push(parents []string) { +func (s *parentStack) push(parents []string) error { for i := 0; i < len(parents); i++ { - s.writeIndent(1) - s.WriteByte('<') - s.WriteString(parents[i]) - s.WriteByte('>') + if err := s.p.writeStart(&StartElement{Name: Name{Local: parents[i]}}); err != nil { + return err + } } s.stack = append(s.stack, parents...) + return nil } // A MarshalXMLError is returned when Marshal encounters a type diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go index ca14a1e..d34118a 100644 --- a/libgo/go/encoding/xml/marshal_test.go +++ b/libgo/go/encoding/xml/marshal_test.go @@ -276,6 +276,54 @@ type Strings struct { X []string `xml:"A>B,omitempty"` } +type PointerFieldsTest struct { + XMLName Name `xml:"dummy"` + Name *string `xml:"name,attr"` + Age *uint `xml:"age,attr"` + Empty *string `xml:"empty,attr"` + Contents *string `xml:",chardata"` +} + +type ChardataEmptyTest struct { + XMLName Name `xml:"test"` + Contents *string `xml:",chardata"` +} + +type MyMarshalerTest struct { +} + +var _ Marshaler = (*MyMarshalerTest)(nil) + +func (m *MyMarshalerTest) MarshalXML(e *Encoder, start StartElement) error { + e.EncodeToken(start) + e.EncodeToken(CharData([]byte("hello world"))) + e.EncodeToken(EndElement{start.Name}) + return nil +} + +type MyMarshalerAttrTest struct { +} + +var _ MarshalerAttr = (*MyMarshalerAttrTest)(nil) + +func (m *MyMarshalerAttrTest) MarshalXMLAttr(name Name) (Attr, error) { + return Attr{name, "hello world"}, nil +} + +type MarshalerStruct struct { + Foo MyMarshalerAttrTest `xml:",attr"` +} + +func ifaceptr(x interface{}) interface{} { + return &x +} + +var ( + nameAttr = "Sarah" + ageAttr = uint(12) + contentsAttr = "lorem ipsum" +) + // Unless explicitly stated as such (or *Plain), all of the // tests below are two-way tests. When introducing new tests, // please try to make them two-way as well to ensure that @@ -312,6 +360,7 @@ var marshalTests = []struct { {Value: &Plain{NamedType("potato")}, ExpectXML: `<Plain><V>potato</V></Plain>`}, {Value: &Plain{[]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, {Value: &Plain{[3]int{1, 2, 3}}, ExpectXML: `<Plain><V>1</V><V>2</V><V>3</V></Plain>`}, + {Value: ifaceptr(true), MarshalOnly: true, ExpectXML: `<bool>true</bool>`}, // Test time. { @@ -673,6 +722,20 @@ var marshalTests = []struct { ExpectXML: `<OmitAttrTest></OmitAttrTest>`, }, + // pointer fields + { + Value: &PointerFieldsTest{Name: &nameAttr, Age: &ageAttr, Contents: &contentsAttr}, + ExpectXML: `<dummy name="Sarah" age="12">lorem ipsum</dummy>`, + MarshalOnly: true, + }, + + // empty chardata pointer field + { + Value: &ChardataEmptyTest{}, + ExpectXML: `<test></test>`, + MarshalOnly: true, + }, + // omitempty on fields { Value: &OmitFieldTest{ @@ -811,6 +874,15 @@ var marshalTests = []struct { ExpectXML: `<Strings><A></A></Strings>`, Value: &Strings{}, }, + // Custom marshalers. + { + ExpectXML: `<MyMarshalerTest>hello world</MyMarshalerTest>`, + Value: &MyMarshalerTest{}, + }, + { + ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`, + Value: &MarshalerStruct{}, + }, } func TestMarshal(t *testing.T) { @@ -837,6 +909,10 @@ type AttrParent struct { X string `xml:"X>Y,attr"` } +type BadAttr struct { + Name []string `xml:"name,attr"` +} + var marshalErrorTests = []struct { Value interface{} Err string @@ -869,6 +945,10 @@ var marshalErrorTests = []struct { Value: &AttrParent{}, Err: `xml: X>Y chain not valid with attr flag`, }, + { + Value: BadAttr{[]string{"X", "Y"}}, + Err: `xml: unsupported type: []string`, + }, } var marshalIndentTests = []struct { @@ -1009,6 +1089,23 @@ func TestMarshalWriteIOErrors(t *testing.T) { } } +func TestMarshalFlush(t *testing.T) { + var buf bytes.Buffer + enc := NewEncoder(&buf) + if err := enc.EncodeToken(CharData("hello world")); err != nil { + t.Fatalf("enc.EncodeToken: %v", err) + } + if buf.Len() > 0 { + t.Fatalf("enc.EncodeToken caused actual write: %q", buf.Bytes()) + } + if err := enc.Flush(); err != nil { + t.Fatalf("enc.Flush: %v", err) + } + if buf.String() != "hello world" { + t.Fatalf("after enc.Flush, buf.String() = %q, want %q", buf.String(), "hello world") + } +} + func BenchmarkMarshal(b *testing.B) { for i := 0; i < b.N; i++ { Marshal(atomValue) @@ -1021,3 +1118,34 @@ func BenchmarkUnmarshal(b *testing.B) { Unmarshal(xml, &Feed{}) } } + +// golang.org/issue/6556 +func TestStructPointerMarshal(t *testing.T) { + type A struct { + XMLName string `xml:"a"` + B []interface{} + } + type C struct { + XMLName Name + Value string `xml:"value"` + } + + a := new(A) + a.B = append(a.B, &C{ + XMLName: Name{Local: "c"}, + Value: "x", + }) + + b, err := Marshal(a) + if err != nil { + t.Fatal(err) + } + if x := string(b); x != "<a><c><value>x</value></c></a>" { + t.Fatal(x) + } + var v A + err = Unmarshal(b, &v) + if err != nil { + t.Fatal(err) + } +} diff --git a/libgo/go/encoding/xml/read.go b/libgo/go/encoding/xml/read.go index a7a2a96..da7ad3b 100644 --- a/libgo/go/encoding/xml/read.go +++ b/libgo/go/encoding/xml/read.go @@ -6,11 +6,12 @@ package xml import ( "bytes" + "encoding" "errors" + "fmt" "reflect" "strconv" "strings" - "time" ) // BUG(rsc): Mapping between XML elements and data structures is inherently flawed: @@ -57,7 +58,7 @@ import ( // If there is no such field, the character data is discarded. // // * If the XML element contains comments, they are accumulated in -// the first struct field that has tag ",comments". The struct +// the first struct field that has tag ",comment". The struct // field may have type []byte or string. If there is no such // field, the comments are discarded. // @@ -137,6 +138,136 @@ type UnmarshalError string func (e UnmarshalError) Error() string { return string(e) } +// Unmarshaler is the interface implemented by objects that can unmarshal +// an XML element description of themselves. +// +// UnmarshalXML decodes a single XML element +// beginning with the given start element. +// If it returns an error, the outer call to Unmarshal stops and +// returns that error. +// UnmarshalXML must consume exactly one XML element. +// One common implementation strategy is to unmarshal into +// a separate value with a layout matching the expected XML +// using d.DecodeElement, and then to copy the data from +// that value into the receiver. +// Another common strategy is to use d.Token to process the +// XML object one token at a time. +// UnmarshalXML may not use d.RawToken. +type Unmarshaler interface { + UnmarshalXML(d *Decoder, start StartElement) error +} + +// UnmarshalerAttr is the interface implemented by objects that can unmarshal +// an XML attribute description of themselves. +// +// UnmarshalXMLAttr decodes a single XML attribute. +// If it returns an error, the outer call to Unmarshal stops and +// returns that error. +// UnmarshalXMLAttr is used only for struct fields with the +// "attr" option in the field tag. +type UnmarshalerAttr interface { + UnmarshalXMLAttr(attr Attr) error +} + +// receiverType returns the receiver type to use in an expression like "%s.MethodName". +func receiverType(val interface{}) string { + t := reflect.TypeOf(val) + if t.Name() != "" { + return t.String() + } + return "(" + t.String() + ")" +} + +// unmarshalInterface unmarshals a single XML element into val. +// start is the opening tag of the element. +func (p *Decoder) unmarshalInterface(val Unmarshaler, start *StartElement) error { + // Record that decoder must stop at end tag corresponding to start. + p.pushEOF() + + p.unmarshalDepth++ + err := val.UnmarshalXML(p, *start) + p.unmarshalDepth-- + if err != nil { + p.popEOF() + return err + } + + if !p.popEOF() { + return fmt.Errorf("xml: %s.UnmarshalXML did not consume entire <%s> element", receiverType(val), start.Name.Local) + } + + return nil +} + +// unmarshalTextInterface unmarshals a single XML element into val. +// The chardata contained in the element (but not its children) +// is passed to the text unmarshaler. +func (p *Decoder) unmarshalTextInterface(val encoding.TextUnmarshaler, start *StartElement) error { + var buf []byte + depth := 1 + for depth > 0 { + t, err := p.Token() + if err != nil { + return err + } + switch t := t.(type) { + case CharData: + if depth == 1 { + buf = append(buf, t...) + } + case StartElement: + depth++ + case EndElement: + depth-- + } + } + return val.UnmarshalText(buf) +} + +// unmarshalAttr unmarshals a single XML attribute into val. +func (p *Decoder) unmarshalAttr(val reflect.Value, attr Attr) error { + if val.Kind() == reflect.Ptr { + if val.IsNil() { + val.Set(reflect.New(val.Type().Elem())) + } + val = val.Elem() + } + + if val.CanInterface() && val.Type().Implements(unmarshalerAttrType) { + // This is an unmarshaler with a non-pointer receiver, + // so it's likely to be incorrect, but we do what we're told. + return val.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr) + } + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() && pv.Type().Implements(unmarshalerAttrType) { + return pv.Interface().(UnmarshalerAttr).UnmarshalXMLAttr(attr) + } + } + + // Not an UnmarshalerAttr; try encoding.TextUnmarshaler. + if val.CanInterface() && val.Type().Implements(textUnmarshalerType) { + // This is an unmarshaler with a non-pointer receiver, + // so it's likely to be incorrect, but we do what we're told. + return val.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value)) + } + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) { + return pv.Interface().(encoding.TextUnmarshaler).UnmarshalText([]byte(attr.Value)) + } + } + + copyValue(val, []byte(attr.Value)) + return nil +} + +var ( + unmarshalerType = reflect.TypeOf((*Unmarshaler)(nil)).Elem() + unmarshalerAttrType = reflect.TypeOf((*UnmarshalerAttr)(nil)).Elem() + textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() +) + // Unmarshal a single XML element into val. func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { // Find start element if we need it. @@ -153,11 +284,35 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { } } - if pv := val; pv.Kind() == reflect.Ptr { - if pv.IsNil() { - pv.Set(reflect.New(pv.Type().Elem())) + if val.Kind() == reflect.Ptr { + if val.IsNil() { + val.Set(reflect.New(val.Type().Elem())) + } + val = val.Elem() + } + + if val.CanInterface() && val.Type().Implements(unmarshalerType) { + // This is an unmarshaler with a non-pointer receiver, + // so it's likely to be incorrect, but we do what we're told. + return p.unmarshalInterface(val.Interface().(Unmarshaler), start) + } + + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() && pv.Type().Implements(unmarshalerType) { + return p.unmarshalInterface(pv.Interface().(Unmarshaler), start) + } + } + + if val.CanInterface() && val.Type().Implements(textUnmarshalerType) { + return p.unmarshalTextInterface(val.Interface().(encoding.TextUnmarshaler), start) + } + + if val.CanAddr() { + pv := val.Addr() + if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) { + return p.unmarshalTextInterface(pv.Interface().(encoding.TextUnmarshaler), start) } - val = pv.Elem() } var ( @@ -222,10 +377,6 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { v.Set(reflect.ValueOf(start.Name)) break } - if typ == timeType { - saveData = v - break - } sv = v tinfo, err = getTypeInfo(typ) @@ -264,7 +415,9 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error { // Look for attribute. for _, a := range start.Attr { if a.Name.Local == finfo.name && (finfo.xmlns == "" || finfo.xmlns == a.Name.Space) { - copyValue(strv, []byte(a.Value)) + if err := p.unmarshalAttr(strv, a); err != nil { + return err + } break } } @@ -352,6 +505,23 @@ Loop: } } + if saveData.IsValid() && saveData.CanInterface() && saveData.Type().Implements(textUnmarshalerType) { + if err := saveData.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { + return err + } + saveData = reflect.Value{} + } + + if saveData.IsValid() && saveData.CanAddr() { + pv := saveData.Addr() + if pv.CanInterface() && pv.Type().Implements(textUnmarshalerType) { + if err := pv.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { + return err + } + saveData = reflect.Value{} + } + } + if err := copyValue(saveData, data); err != nil { return err } @@ -374,6 +544,8 @@ Loop: } func copyValue(dst reflect.Value, src []byte) (err error) { + dst0 := dst + if dst.Kind() == reflect.Ptr { if dst.IsNil() { dst.Set(reflect.New(dst.Type().Elem())) @@ -384,9 +556,9 @@ func copyValue(dst reflect.Value, src []byte) (err error) { // Save accumulated data. switch dst.Kind() { case reflect.Invalid: - // Probably a commendst. + // Probably a comment. default: - return errors.New("cannot happen: unknown type " + dst.Type().String()) + return errors.New("cannot unmarshal into " + dst0.Type().String()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: itmp, err := strconv.ParseInt(string(src), 10, dst.Type().Bits()) if err != nil { @@ -419,14 +591,6 @@ func copyValue(dst reflect.Value, src []byte) (err error) { src = []byte{} } dst.SetBytes(src) - case reflect.Struct: - if dst.Type() == timeType { - tv, err := time.Parse(time.RFC3339, string(src)) - if err != nil { - return err - } - dst.Set(reflect.ValueOf(tv)) - } } return nil } diff --git a/libgo/go/encoding/xml/read_test.go b/libgo/go/encoding/xml/read_test.go index 7d28c5d..1404c90 100644 --- a/libgo/go/encoding/xml/read_test.go +++ b/libgo/go/encoding/xml/read_test.go @@ -5,6 +5,7 @@ package xml import ( + "io" "reflect" "strings" "testing" @@ -621,3 +622,66 @@ func TestMarshalNSAttr(t *testing.T) { t.Errorf("Unmarshal = %q, want %q", dst, src) } } + +type MyCharData struct { + body string +} + +func (m *MyCharData) UnmarshalXML(d *Decoder, start StartElement) error { + for { + t, err := d.Token() + if err == io.EOF { // found end of element + break + } + if err != nil { + return err + } + if char, ok := t.(CharData); ok { + m.body += string(char) + } + } + return nil +} + +var _ Unmarshaler = (*MyCharData)(nil) + +func (m *MyCharData) UnmarshalXMLAttr(attr Attr) error { + panic("must not call") +} + +type MyAttr struct { + attr string +} + +func (m *MyAttr) UnmarshalXMLAttr(attr Attr) error { + m.attr = attr.Value + return nil +} + +var _ UnmarshalerAttr = (*MyAttr)(nil) + +type MyStruct struct { + Data *MyCharData + Attr *MyAttr `xml:",attr"` + + Data2 MyCharData + Attr2 MyAttr `xml:",attr"` +} + +func TestUnmarshaler(t *testing.T) { + xml := `<?xml version="1.0" encoding="utf-8"?> + <MyStruct Attr="attr1" Attr2="attr2"> + <Data>hello <!-- comment -->world</Data> + <Data2>howdy <!-- comment -->world</Data2> + </MyStruct> + ` + + var m MyStruct + if err := Unmarshal([]byte(xml), &m); err != nil { + t.Fatal(err) + } + + if m.Data == nil || m.Attr == nil || m.Data.body != "hello world" || m.Attr.attr != "attr1" || m.Data2.body != "howdy world" || m.Attr2.attr != "attr2" { + t.Errorf("m=%#+v\n", m) + } +} diff --git a/libgo/go/encoding/xml/xml.go b/libgo/go/encoding/xml/xml.go index 021f7e4..5b9d670 100644 --- a/libgo/go/encoding/xml/xml.go +++ b/libgo/go/encoding/xml/xml.go @@ -16,6 +16,7 @@ package xml import ( "bufio" "bytes" + "errors" "fmt" "io" "strconv" @@ -66,6 +67,11 @@ func (e StartElement) Copy() StartElement { return e } +// End returns the corresponding XML end element. +func (e StartElement) End() EndElement { + return EndElement{e.Name} +} + // An EndElement represents an XML end element. type EndElement struct { Name Name @@ -144,6 +150,10 @@ type Decoder struct { // d.Entity = HTMLEntity // // creates a parser that can handle typical HTML. + // + // Strict mode does not enforce the requirements of the XML name spaces TR. + // In particular it does not reject name space tags using undefined prefixes. + // Such tags are recorded with the unknown prefix as the name space URL. Strict bool // When Strict == false, AutoClose indicates a set of elements to @@ -174,18 +184,19 @@ type Decoder struct { // the attribute xmlns="DefaultSpace". DefaultSpace string - r io.ByteReader - buf bytes.Buffer - saved *bytes.Buffer - stk *stack - free *stack - needClose bool - toClose Name - nextToken Token - nextByte int - ns map[string]string - err error - line int + r io.ByteReader + buf bytes.Buffer + saved *bytes.Buffer + stk *stack + free *stack + needClose bool + toClose Name + nextToken Token + nextByte int + ns map[string]string + err error + line int + unmarshalDepth int } // NewDecoder creates a new XML parser reading from r. @@ -223,10 +234,14 @@ func NewDecoder(r io.Reader) *Decoder { // If Token encounters an unrecognized name space prefix, // it uses the prefix as the Space rather than report an error. func (d *Decoder) Token() (t Token, err error) { + if d.stk != nil && d.stk.kind == stkEOF { + err = io.EOF + return + } if d.nextToken != nil { t = d.nextToken d.nextToken = nil - } else if t, err = d.RawToken(); err != nil { + } else if t, err = d.rawToken(); err != nil { return } @@ -322,6 +337,7 @@ type stack struct { const ( stkStart = iota stkNs + stkEOF ) func (d *Decoder) push(kind int) *stack { @@ -347,6 +363,43 @@ func (d *Decoder) pop() *stack { return s } +// Record that after the current element is finished +// (that element is already pushed on the stack) +// Token should return EOF until popEOF is called. +func (d *Decoder) pushEOF() { + // Walk down stack to find Start. + // It might not be the top, because there might be stkNs + // entries above it. + start := d.stk + for start.kind != stkStart { + start = start.next + } + // The stkNs entries below a start are associated with that + // element too; skip over them. + for start.next != nil && start.next.kind == stkNs { + start = start.next + } + s := d.free + if s != nil { + d.free = s.next + } else { + s = new(stack) + } + s.kind = stkEOF + s.next = start.next + start.next = s +} + +// Undo a pushEOF. +// The element must have been finished, so the EOF should be at the top of the stack. +func (d *Decoder) popEOF() bool { + if d.stk == nil || d.stk.kind != stkEOF { + return false + } + d.pop() + return true +} + // Record that we are starting an element with the given name. func (d *Decoder) pushElement(name Name) { s := d.push(stkStart) @@ -395,9 +448,9 @@ func (d *Decoder) popElement(t *EndElement) bool { return false } - // Pop stack until a Start is on the top, undoing the + // Pop stack until a Start or EOF is on the top, undoing the // translations that were associated with the element we just closed. - for d.stk != nil && d.stk.kind != stkStart { + for d.stk != nil && d.stk.kind != stkStart && d.stk.kind != stkEOF { s := d.pop() if s.ok { d.ns[s.name.Local] = s.name.Space @@ -429,10 +482,19 @@ func (d *Decoder) autoClose(t Token) (Token, bool) { return nil, false } +var errRawToken = errors.New("xml: cannot use RawToken from UnmarshalXML method") + // RawToken is like Token but does not verify that // start and end elements match and does not translate // name space prefixes to their corresponding URLs. func (d *Decoder) RawToken() (Token, error) { + if d.unmarshalDepth > 0 { + return nil, errRawToken + } + return d.rawToken() +} + +func (d *Decoder) rawToken() (Token, error) { if d.err != nil { return nil, d.err } @@ -484,8 +546,7 @@ func (d *Decoder) RawToken() (Token, error) { case '?': // <?: Processing instruction. - // TODO(rsc): Should parse the <?xml declaration to make sure - // the version is 1.0 and the encoding is UTF-8. + // TODO(rsc): Should parse the <?xml declaration to make sure the version is 1.0. var target string if target, ok = d.name(); !ok { if d.err == nil { @@ -1112,6 +1173,30 @@ func isName(s []byte) bool { return true } +func isNameString(s string) bool { + if len(s) == 0 { + return false + } + c, n := utf8.DecodeRuneInString(s) + if c == utf8.RuneError && n == 1 { + return false + } + if !unicode.Is(first, c) { + return false + } + for n < len(s) { + s = s[n:] + c, n = utf8.DecodeRuneInString(s) + if c == utf8.RuneError && n == 1 { + return false + } + if !unicode.Is(first, c) && !unicode.Is(second, c) { + return false + } + } + return true +} + // These tables were generated by cut and paste from Appendix B of // the XML spec at http://www.xml.com/axml/testaxml.htm // and then reformatting. First corresponds to (Letter | '_' | ':') @@ -1758,7 +1843,7 @@ func EscapeText(w io.Writer, s []byte) error { case '\r': esc = esc_cr default: - if !isInCharacterRange(r) { + if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) { esc = esc_fffd break } @@ -1778,6 +1863,45 @@ func EscapeText(w io.Writer, s []byte) error { return nil } +// EscapeString writes to p the properly escaped XML equivalent +// of the plain text data s. +func (p *printer) EscapeString(s string) { + var esc []byte + last := 0 + for i := 0; i < len(s); { + r, width := utf8.DecodeRuneInString(s[i:]) + i += width + switch r { + case '"': + esc = esc_quot + case '\'': + esc = esc_apos + case '&': + esc = esc_amp + case '<': + esc = esc_lt + case '>': + esc = esc_gt + case '\t': + esc = esc_tab + case '\n': + esc = esc_nl + case '\r': + esc = esc_cr + default: + if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) { + esc = esc_fffd + break + } + continue + } + p.WriteString(s[last : i-width]) + p.Write(esc) + last = i + } + p.WriteString(s[last:]) +} + // Escape is like EscapeText but omits the error return value. // It is provided for backwards compatibility with Go 1.0. // Code targeting Go 1.1 or later should use EscapeText. diff --git a/libgo/go/encoding/xml/xml_test.go b/libgo/go/encoding/xml/xml_test.go index eeedbe5..7723ab1 100644 --- a/libgo/go/encoding/xml/xml_test.go +++ b/libgo/go/encoding/xml/xml_test.go @@ -11,6 +11,7 @@ import ( "reflect" "strings" "testing" + "unicode/utf8" ) const testInput = ` @@ -246,10 +247,8 @@ func (d *downCaser) Read(p []byte) (int, error) { } func TestRawTokenAltEncoding(t *testing.T) { - sawEncoding := "" d := NewDecoder(strings.NewReader(testInputAltEncoding)) d.CharsetReader = func(charset string, input io.Reader) (io.Reader, error) { - sawEncoding = charset if charset != "x-testing-uppercase" { t.Fatalf("unexpected charset %q", charset) } @@ -714,3 +713,14 @@ func TestEscapeTextInvalidChar(t *testing.T) { t.Errorf("have %v, want %v", text, expected) } } + +func TestIssue5880(t *testing.T) { + type T []byte + data, err := Marshal(T{192, 168, 0, 1}) + if err != nil { + t.Errorf("Marshal error: %v", err) + } + if !utf8.Valid(data) { + t.Errorf("Marshal generated invalid UTF-8: %x", data) + } +} diff --git a/libgo/go/flag/export_test.go b/libgo/go/flag/export_test.go index 7b19080..56cda58 100644 --- a/libgo/go/flag/export_test.go +++ b/libgo/go/flag/export_test.go @@ -12,11 +12,6 @@ import "os" // After calling ResetForTesting, parse errors in flag handling will not // exit the program. func ResetForTesting(usage func()) { - commandLine = NewFlagSet(os.Args[0], ContinueOnError) + CommandLine = NewFlagSet(os.Args[0], ContinueOnError) Usage = usage } - -// CommandLine returns the default FlagSet. -func CommandLine() *FlagSet { - return commandLine -} diff --git a/libgo/go/flag/flag.go b/libgo/go/flag/flag.go index 85dd8c3..e7c863e 100644 --- a/libgo/go/flag/flag.go +++ b/libgo/go/flag/flag.go @@ -89,6 +89,8 @@ func (b *boolValue) Set(s string) error { return err } +func (b *boolValue) Get() interface{} { return bool(*b) } + func (b *boolValue) String() string { return fmt.Sprintf("%v", *b) } func (b *boolValue) IsBoolFlag() bool { return true } @@ -114,6 +116,8 @@ func (i *intValue) Set(s string) error { return err } +func (i *intValue) Get() interface{} { return int(*i) } + func (i *intValue) String() string { return fmt.Sprintf("%v", *i) } // -- int64 Value @@ -130,6 +134,8 @@ func (i *int64Value) Set(s string) error { return err } +func (i *int64Value) Get() interface{} { return int64(*i) } + func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) } // -- uint Value @@ -146,6 +152,8 @@ func (i *uintValue) Set(s string) error { return err } +func (i *uintValue) Get() interface{} { return uint(*i) } + func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) } // -- uint64 Value @@ -162,6 +170,8 @@ func (i *uint64Value) Set(s string) error { return err } +func (i *uint64Value) Get() interface{} { return uint64(*i) } + func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) } // -- string Value @@ -177,6 +187,8 @@ func (s *stringValue) Set(val string) error { return nil } +func (s *stringValue) Get() interface{} { return string(*s) } + func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) } // -- float64 Value @@ -193,6 +205,8 @@ func (f *float64Value) Set(s string) error { return err } +func (f *float64Value) Get() interface{} { return float64(*f) } + func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) } // -- time.Duration Value @@ -209,6 +223,8 @@ func (d *durationValue) Set(s string) error { return err } +func (d *durationValue) Get() interface{} { return time.Duration(*d) } + func (d *durationValue) String() string { return (*time.Duration)(d).String() } // Value is the interface to the dynamic value stored in a flag. @@ -222,6 +238,15 @@ type Value interface { Set(string) error } +// Getter is an interface that allows the contents of a Value to be retrieved. +// It wraps the Value interface, rather than being part of it, because it +// appeared after Go 1 and its compatibility rules. All Value types provided +// by this package satisfy the Getter interface. +type Getter interface { + Value + Get() interface{} +} + // ErrorHandling defines how to handle flag parsing errors. type ErrorHandling int @@ -231,7 +256,8 @@ const ( PanicOnError ) -// A FlagSet represents a set of defined flags. +// A FlagSet represents a set of defined flags. The zero value of a FlagSet +// has no name and has ContinueOnError error handling. type FlagSet struct { // Usage is the function called when an error occurs while parsing flags. // The field is a function (not a method) that may be changed to point to @@ -296,7 +322,7 @@ func (f *FlagSet) VisitAll(fn func(*Flag)) { // VisitAll visits the command-line flags in lexicographical order, calling // fn for each. It visits all flags, even those not set. func VisitAll(fn func(*Flag)) { - commandLine.VisitAll(fn) + CommandLine.VisitAll(fn) } // Visit visits the flags in lexicographical order, calling fn for each. @@ -310,7 +336,7 @@ func (f *FlagSet) Visit(fn func(*Flag)) { // Visit visits the command-line flags in lexicographical order, calling fn // for each. It visits only those flags that have been set. func Visit(fn func(*Flag)) { - commandLine.Visit(fn) + CommandLine.Visit(fn) } // Lookup returns the Flag structure of the named flag, returning nil if none exists. @@ -321,7 +347,7 @@ func (f *FlagSet) Lookup(name string) *Flag { // Lookup returns the Flag structure of the named command-line flag, // returning nil if none exists. func Lookup(name string) *Flag { - return commandLine.formal[name] + return CommandLine.formal[name] } // Set sets the value of the named flag. @@ -343,7 +369,7 @@ func (f *FlagSet) Set(name, value string) error { // Set sets the value of the named command-line flag. func Set(name, value string) error { - return commandLine.Set(name, value) + return CommandLine.Set(name, value) } // PrintDefaults prints, to standard error unless configured @@ -361,16 +387,20 @@ func (f *FlagSet) PrintDefaults() { // PrintDefaults prints to standard error the default values of all defined command-line flags. func PrintDefaults() { - commandLine.PrintDefaults() + CommandLine.PrintDefaults() } // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { - fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + if f.name == "" { + fmt.Fprintf(f.out(), "Usage:\n") + } else { + fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + } f.PrintDefaults() } -// NOTE: Usage is not just defaultUsage(commandLine) +// NOTE: Usage is not just defaultUsage(CommandLine) // because it serves (via godoc flag Usage) as the example // for how to write your own usage function. @@ -385,7 +415,7 @@ var Usage = func() { func (f *FlagSet) NFlag() int { return len(f.actual) } // NFlag returns the number of command-line flags that have been set. -func NFlag() int { return len(commandLine.actual) } +func NFlag() int { return len(CommandLine.actual) } // Arg returns the i'th argument. Arg(0) is the first remaining argument // after flags have been processed. @@ -399,20 +429,20 @@ func (f *FlagSet) Arg(i int) string { // Arg returns the i'th command-line argument. Arg(0) is the first remaining argument // after flags have been processed. func Arg(i int) string { - return commandLine.Arg(i) + return CommandLine.Arg(i) } // NArg is the number of arguments remaining after flags have been processed. func (f *FlagSet) NArg() int { return len(f.args) } // NArg is the number of arguments remaining after flags have been processed. -func NArg() int { return len(commandLine.args) } +func NArg() int { return len(CommandLine.args) } // Args returns the non-flag arguments. func (f *FlagSet) Args() []string { return f.args } // Args returns the non-flag command-line arguments. -func Args() []string { return commandLine.args } +func Args() []string { return CommandLine.args } // BoolVar defines a bool flag with specified name, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. @@ -423,7 +453,7 @@ func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string) { // BoolVar defines a bool flag with specified name, default value, and usage string. // The argument p points to a bool variable in which to store the value of the flag. func BoolVar(p *bool, name string, value bool, usage string) { - commandLine.Var(newBoolValue(value, p), name, usage) + CommandLine.Var(newBoolValue(value, p), name, usage) } // Bool defines a bool flag with specified name, default value, and usage string. @@ -437,7 +467,7 @@ func (f *FlagSet) Bool(name string, value bool, usage string) *bool { // Bool defines a bool flag with specified name, default value, and usage string. // The return value is the address of a bool variable that stores the value of the flag. func Bool(name string, value bool, usage string) *bool { - return commandLine.Bool(name, value, usage) + return CommandLine.Bool(name, value, usage) } // IntVar defines an int flag with specified name, default value, and usage string. @@ -449,7 +479,7 @@ func (f *FlagSet) IntVar(p *int, name string, value int, usage string) { // IntVar defines an int flag with specified name, default value, and usage string. // The argument p points to an int variable in which to store the value of the flag. func IntVar(p *int, name string, value int, usage string) { - commandLine.Var(newIntValue(value, p), name, usage) + CommandLine.Var(newIntValue(value, p), name, usage) } // Int defines an int flag with specified name, default value, and usage string. @@ -463,7 +493,7 @@ func (f *FlagSet) Int(name string, value int, usage string) *int { // Int defines an int flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. func Int(name string, value int, usage string) *int { - return commandLine.Int(name, value, usage) + return CommandLine.Int(name, value, usage) } // Int64Var defines an int64 flag with specified name, default value, and usage string. @@ -475,7 +505,7 @@ func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string) { // Int64Var defines an int64 flag with specified name, default value, and usage string. // The argument p points to an int64 variable in which to store the value of the flag. func Int64Var(p *int64, name string, value int64, usage string) { - commandLine.Var(newInt64Value(value, p), name, usage) + CommandLine.Var(newInt64Value(value, p), name, usage) } // Int64 defines an int64 flag with specified name, default value, and usage string. @@ -489,7 +519,7 @@ func (f *FlagSet) Int64(name string, value int64, usage string) *int64 { // Int64 defines an int64 flag with specified name, default value, and usage string. // The return value is the address of an int64 variable that stores the value of the flag. func Int64(name string, value int64, usage string) *int64 { - return commandLine.Int64(name, value, usage) + return CommandLine.Int64(name, value, usage) } // UintVar defines a uint flag with specified name, default value, and usage string. @@ -501,7 +531,7 @@ func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string) { // UintVar defines a uint flag with specified name, default value, and usage string. // The argument p points to a uint variable in which to store the value of the flag. func UintVar(p *uint, name string, value uint, usage string) { - commandLine.Var(newUintValue(value, p), name, usage) + CommandLine.Var(newUintValue(value, p), name, usage) } // Uint defines a uint flag with specified name, default value, and usage string. @@ -515,7 +545,7 @@ func (f *FlagSet) Uint(name string, value uint, usage string) *uint { // Uint defines a uint flag with specified name, default value, and usage string. // The return value is the address of a uint variable that stores the value of the flag. func Uint(name string, value uint, usage string) *uint { - return commandLine.Uint(name, value, usage) + return CommandLine.Uint(name, value, usage) } // Uint64Var defines a uint64 flag with specified name, default value, and usage string. @@ -527,7 +557,7 @@ func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string) // Uint64Var defines a uint64 flag with specified name, default value, and usage string. // The argument p points to a uint64 variable in which to store the value of the flag. func Uint64Var(p *uint64, name string, value uint64, usage string) { - commandLine.Var(newUint64Value(value, p), name, usage) + CommandLine.Var(newUint64Value(value, p), name, usage) } // Uint64 defines a uint64 flag with specified name, default value, and usage string. @@ -541,7 +571,7 @@ func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64 { // Uint64 defines a uint64 flag with specified name, default value, and usage string. // The return value is the address of a uint64 variable that stores the value of the flag. func Uint64(name string, value uint64, usage string) *uint64 { - return commandLine.Uint64(name, value, usage) + return CommandLine.Uint64(name, value, usage) } // StringVar defines a string flag with specified name, default value, and usage string. @@ -553,7 +583,7 @@ func (f *FlagSet) StringVar(p *string, name string, value string, usage string) // StringVar defines a string flag with specified name, default value, and usage string. // The argument p points to a string variable in which to store the value of the flag. func StringVar(p *string, name string, value string, usage string) { - commandLine.Var(newStringValue(value, p), name, usage) + CommandLine.Var(newStringValue(value, p), name, usage) } // String defines a string flag with specified name, default value, and usage string. @@ -567,7 +597,7 @@ func (f *FlagSet) String(name string, value string, usage string) *string { // String defines a string flag with specified name, default value, and usage string. // The return value is the address of a string variable that stores the value of the flag. func String(name string, value string, usage string) *string { - return commandLine.String(name, value, usage) + return CommandLine.String(name, value, usage) } // Float64Var defines a float64 flag with specified name, default value, and usage string. @@ -579,7 +609,7 @@ func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage strin // Float64Var defines a float64 flag with specified name, default value, and usage string. // The argument p points to a float64 variable in which to store the value of the flag. func Float64Var(p *float64, name string, value float64, usage string) { - commandLine.Var(newFloat64Value(value, p), name, usage) + CommandLine.Var(newFloat64Value(value, p), name, usage) } // Float64 defines a float64 flag with specified name, default value, and usage string. @@ -593,7 +623,7 @@ func (f *FlagSet) Float64(name string, value float64, usage string) *float64 { // Float64 defines a float64 flag with specified name, default value, and usage string. // The return value is the address of a float64 variable that stores the value of the flag. func Float64(name string, value float64, usage string) *float64 { - return commandLine.Float64(name, value, usage) + return CommandLine.Float64(name, value, usage) } // DurationVar defines a time.Duration flag with specified name, default value, and usage string. @@ -605,7 +635,7 @@ func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration // DurationVar defines a time.Duration flag with specified name, default value, and usage string. // The argument p points to a time.Duration variable in which to store the value of the flag. func DurationVar(p *time.Duration, name string, value time.Duration, usage string) { - commandLine.Var(newDurationValue(value, p), name, usage) + CommandLine.Var(newDurationValue(value, p), name, usage) } // Duration defines a time.Duration flag with specified name, default value, and usage string. @@ -619,7 +649,7 @@ func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time // Duration defines a time.Duration flag with specified name, default value, and usage string. // The return value is the address of a time.Duration variable that stores the value of the flag. func Duration(name string, value time.Duration, usage string) *time.Duration { - return commandLine.Duration(name, value, usage) + return CommandLine.Duration(name, value, usage) } // Var defines a flag with the specified name and usage string. The type and @@ -633,7 +663,12 @@ func (f *FlagSet) Var(value Value, name string, usage string) { flag := &Flag{name, usage, value, value.String()} _, alreadythere := f.formal[name] if alreadythere { - msg := fmt.Sprintf("%s flag redefined: %s", f.name, name) + var msg string + if f.name == "" { + msg = fmt.Sprintf("flag redefined: %s", name) + } else { + msg = fmt.Sprintf("%s flag redefined: %s", f.name, name) + } fmt.Fprintln(f.out(), msg) panic(msg) // Happens only if flags are declared with identical names } @@ -650,7 +685,7 @@ func (f *FlagSet) Var(value Value, name string, usage string) { // of strings by giving the slice the methods of Value; in particular, Set would // decompose the comma-separated string into the slice. func Var(value Value, name string, usage string) { - commandLine.Var(value, name, usage) + CommandLine.Var(value, name, usage) } // failf prints to standard error a formatted error and usage message and @@ -663,9 +698,9 @@ func (f *FlagSet) failf(format string, a ...interface{}) error { } // usage calls the Usage method for the flag set, or the usage function if -// the flag set is commandLine. +// the flag set is CommandLine. func (f *FlagSet) usage() { - if f == commandLine { + if f == CommandLine { Usage() } else if f.Usage == nil { defaultUsage(f) @@ -674,7 +709,7 @@ func (f *FlagSet) usage() { } } -// parseOne parses one flag. It returns whether a flag was seen. +// parseOne parses one flag. It reports whether a flag was seen. func (f *FlagSet) parseOne() (bool, error) { if len(f.args) == 0 { return false, nil @@ -781,17 +816,19 @@ func (f *FlagSet) Parsed() bool { // Parse parses the command-line flags from os.Args[1:]. Must be called // after all flags are defined and before flags are accessed by the program. func Parse() { - // Ignore errors; commandLine is set for ExitOnError. - commandLine.Parse(os.Args[1:]) + // Ignore errors; CommandLine is set for ExitOnError. + CommandLine.Parse(os.Args[1:]) } // Parsed returns true if the command-line flags have been parsed. func Parsed() bool { - return commandLine.Parsed() + return CommandLine.Parsed() } -// The default set of command-line flags, parsed from os.Args. -var commandLine = NewFlagSet(os.Args[0], ExitOnError) +// CommandLine is the default set of command-line flags, parsed from os.Args. +// The top-level functions such as BoolVar, Arg, and on are wrappers for the +// methods of CommandLine. +var CommandLine = NewFlagSet(os.Args[0], ExitOnError) // NewFlagSet returns a new, empty flag set with the specified name and // error handling property. diff --git a/libgo/go/flag/flag_test.go b/libgo/go/flag/flag_test.go index ddd54b2..2c03872 100644 --- a/libgo/go/flag/flag_test.go +++ b/libgo/go/flag/flag_test.go @@ -92,10 +92,54 @@ func TestEverything(t *testing.T) { } } +func TestGet(t *testing.T) { + ResetForTesting(nil) + Bool("test_bool", true, "bool value") + Int("test_int", 1, "int value") + Int64("test_int64", 2, "int64 value") + Uint("test_uint", 3, "uint value") + Uint64("test_uint64", 4, "uint64 value") + String("test_string", "5", "string value") + Float64("test_float64", 6, "float64 value") + Duration("test_duration", 7, "time.Duration value") + + visitor := func(f *Flag) { + if len(f.Name) > 5 && f.Name[0:5] == "test_" { + g, ok := f.Value.(Getter) + if !ok { + t.Errorf("Visit: value does not satisfy Getter: %T", f.Value) + return + } + switch f.Name { + case "test_bool": + ok = g.Get() == true + case "test_int": + ok = g.Get() == int(1) + case "test_int64": + ok = g.Get() == int64(2) + case "test_uint": + ok = g.Get() == uint(3) + case "test_uint64": + ok = g.Get() == uint64(4) + case "test_string": + ok = g.Get() == "5" + case "test_float64": + ok = g.Get() == float64(6) + case "test_duration": + ok = g.Get() == time.Duration(7) + } + if !ok { + t.Errorf("Visit: bad value %T(%v) for %s", g.Get(), g.Get(), f.Name) + } + } + } + VisitAll(visitor) +} + func TestUsage(t *testing.T) { called := false ResetForTesting(func() { called = true }) - if CommandLine().Parse([]string{"-x"}) == nil { + if CommandLine.Parse([]string{"-x"}) == nil { t.Error("parse did not fail for unknown flag") } if !called { @@ -171,7 +215,7 @@ func testParse(f *FlagSet, t *testing.T) { func TestParse(t *testing.T) { ResetForTesting(func() { t.Error("bad parse") }) - testParse(CommandLine(), t) + testParse(CommandLine, t) } func TestFlagSetParse(t *testing.T) { @@ -267,7 +311,7 @@ func TestChangingArgs(t *testing.T) { defer func() { os.Args = oldArgs }() os.Args = []string{"cmd", "-before", "subcmd", "-after", "args"} before := Bool("before", false, "") - if err := CommandLine().Parse(os.Args[1:]); err != nil { + if err := CommandLine.Parse(os.Args[1:]); err != nil { t.Fatal(err) } cmd := Arg(0) diff --git a/libgo/go/fmt/doc.go b/libgo/go/fmt/doc.go index b8dd995..095fd03 100644 --- a/libgo/go/fmt/doc.go +++ b/libgo/go/fmt/doc.go @@ -118,6 +118,28 @@ convert the value before recurring: func (x X) String() string { return Sprintf("<%s>", string(x)) } + Explicit argument indexes: + + In Printf, Sprintf, and Fprintf, the default behavior is for each + formatting verb to format successive arguments passed in the call. + However, the notation [n] immediately before the verb indicates that the + nth one-indexed argument is to be formatted instead. The same notation + before a '*' for a width or precision selects the argument index holding + the value. After processing a bracketed expression [n], arguments n+1, + n+2, etc. will be processed unless otherwise directed. + + For example, + fmt.Sprintf("%[2]d %[1]d\n", 11, 22) + will yield "22, 11", while + fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6), + equivalent to + fmt.Sprintf("%6.2f", 12.0), + will yield " 12.00". Because an explicit index affects subsequent verbs, + this notation can be used to print the same values multiple times + by resetting the index for the first argument to be repeated: + fmt.Sprintf("%d %d %#[1]x %#x", 16, 17) + will yield "16 17 0x10 0x11". + Format errors: If an invalid argument is given for a verb, such as providing @@ -133,6 +155,9 @@ Non-int for width or precision: %!(BADWIDTH) or %!(BADPREC) Printf("%*s", 4.5, "hi"): %!(BADWIDTH)hi Printf("%.*s", 4.5, "hi"): %!(BADPREC)hi + Invalid or invalid use of argument index: %!(BADINDEX) + Printf("%*[2]d", 7): %!d(BADINDEX) + Printf("%.[2]d", 7): %!d(BADINDEX) All errors begin with the string "%!" followed sometimes by a single character (the verb) and end with a parenthesized @@ -144,9 +169,9 @@ through the fmt package. For example, if a String method calls panic("bad"), the resulting formatted message will look like - %s(PANIC=bad) + %!s(PANIC=bad) - The %s just shows the print verb in use when the failure + The %!s just shows the print verb in use when the failure occurred. Scanning @@ -190,6 +215,10 @@ stops if it does not, with the return value of the function indicating the number of arguments scanned. + In all the scanning functions, a carriage return followed + immediately by a newline is treated as a plain newline + (\r\n means the same as \n). + In all the scanning functions, if an operand implements method Scan (that is, it implements the Scanner interface) that method will be used to scan the text for that operand. Also, diff --git a/libgo/go/fmt/fmt_test.go b/libgo/go/fmt/fmt_test.go index df9e5a0..bbca2c5 100644 --- a/libgo/go/fmt/fmt_test.go +++ b/libgo/go/fmt/fmt_test.go @@ -110,7 +110,7 @@ var bslice = barray[:] var b byte -var fmttests = []struct { +var fmtTests = []struct { fmt string val interface{} out string @@ -227,6 +227,8 @@ var fmttests = []struct { {"%+.3g", -1.0, "-1"}, {"% .3g", -1.0, "-1"}, {"% .3g", 1.0, " 1"}, + {"%b", float32(1.0), "8388608p-23"}, + {"%b", 1.0, "4503599627370496p-52"}, // complex values {"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"}, @@ -247,6 +249,8 @@ var fmttests = []struct { {"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"}, {"%+.3g", complex64(1 + 2i), "(+1+2i)"}, {"%+.3g", complex128(1 + 2i), "(+1+2i)"}, + {"%b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"}, + {"%b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"}, // erroneous formats {"", 2, "%!(EXTRA int=2)"}, @@ -493,6 +497,17 @@ var fmttests = []struct { // Used to crash because nByte didn't allow for a sign. {"%b", int64(-1 << 63), "-1000000000000000000000000000000000000000000000000000000000000000"}, + // Used to panic. + {"%0100d", 1, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"}, + {"%0100d", -1, "-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001"}, + {"%0.100f", 1.0, "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, + {"%0.100f", -1.0, "-1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, + + // Zero padding floats used to put the minus sign in the middle. + {"%020f", -1.0, "-000000000001.000000"}, + {"%20f", -1.0, " -1.000000"}, + {"%0100f", -1.0, "-00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001.000000"}, + // Complex fmt used to leave the plus flag set for future entries in the array // causing +2+0i and +3+0i instead of 2+0i and 3+0i. {"%v", []complex64{1, 2, 3}, "[(1+0i) (2+0i) (3+0i)]"}, @@ -503,7 +518,7 @@ var fmttests = []struct { } func TestSprintf(t *testing.T) { - for _, tt := range fmttests { + for _, tt := range fmtTests { s := Sprintf(tt.fmt, tt.val) if i := strings.Index(tt.out, "PTR"); i >= 0 { pattern := "PTR" @@ -539,6 +554,55 @@ func TestSprintf(t *testing.T) { } } +type SE []interface{} // slice of empty; notational compactness. + +var reorderTests = []struct { + fmt string + val SE + out string +}{ + {"%[1]d", SE{1}, "1"}, + {"%[2]d", SE{2, 1}, "1"}, + {"%[2]d %[1]d", SE{1, 2}, "2 1"}, + {"%[2]*[1]d", SE{2, 5}, " 2"}, + {"%6.2f", SE{12.0}, " 12.00"}, // Explicit version of next line. + {"%[3]*.[2]*[1]f", SE{12.0, 2, 6}, " 12.00"}, + {"%[1]*.[2]*[3]f", SE{6, 2, 12.0}, " 12.00"}, + {"%10f", SE{12.0}, " 12.000000"}, + {"%[1]*[3]f", SE{10, 99, 12.0}, " 12.000000"}, + {"%.6f", SE{12.0}, "12.000000"}, // Explicit version of next line. + {"%.[1]*[3]f", SE{6, 99, 12.0}, "12.000000"}, + {"%6.f", SE{12.0}, " 12"}, // // Explicit version of next line; empty precision means zero. + {"%[1]*.[3]f", SE{6, 3, 12.0}, " 12"}, + // An actual use! Print the same arguments twice. + {"%d %d %d %#[1]o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015"}, + + // Erroneous cases. + {"%[d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%]d", SE{2, 1}, "%!](int=2)d%!(EXTRA int=1)"}, + {"%[]d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%[-3]d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%[99]d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%[3]", SE{2, 1}, "%!(NOVERB)"}, + {"%[1].2d", SE{5, 6}, "%!d(BADINDEX)"}, + {"%[1]2d", SE{2, 1}, "%!d(BADINDEX)"}, + {"%3.[2]d", SE{7}, "%!d(BADINDEX)"}, + {"%.[2]d", SE{7}, "%!d(BADINDEX)"}, + {"%d %d %d %#[1]o %#o %#o %#o", SE{11, 12, 13}, "11 12 13 013 014 015 %!o(MISSING)"}, + {"%[5]d %[2]d %d", SE{1, 2, 3}, "%!d(BADINDEX) 2 3"}, + {"%d %[3]d %d", SE{1, 2}, "1 %!d(BADINDEX) 2"}, // Erroneous index does not affect sequence. +} + +func TestReorder(t *testing.T) { + for _, tt := range reorderTests { + s := Sprintf(tt.fmt, tt.val...) + if s != tt.out { + t.Errorf("Sprintf(%q, %v) = <%s> want <%s>", tt.fmt, tt.val, s, tt.out) + } else { + } + } +} + func BenchmarkSprintfEmpty(b *testing.B) { for i := 0; i < b.N; i++ { Sprintf("") @@ -607,6 +671,9 @@ var mallocTest = []struct { var _ bytes.Buffer func TestCountMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -832,16 +899,16 @@ var panictests = []struct { }{ // String {"%s", (*Panic)(nil), "<nil>"}, // nil pointer special case - {"%s", Panic{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"}, - {"%s", Panic{3}, "%s(PANIC=3)"}, + {"%s", Panic{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"}, + {"%s", Panic{3}, "%!s(PANIC=3)"}, // GoString {"%#v", (*Panic)(nil), "<nil>"}, // nil pointer special case - {"%#v", Panic{io.ErrUnexpectedEOF}, "%v(PANIC=unexpected EOF)"}, - {"%#v", Panic{3}, "%v(PANIC=3)"}, + {"%#v", Panic{io.ErrUnexpectedEOF}, "%!v(PANIC=unexpected EOF)"}, + {"%#v", Panic{3}, "%!v(PANIC=3)"}, // Format {"%s", (*PanicF)(nil), "<nil>"}, // nil pointer special case - {"%s", PanicF{io.ErrUnexpectedEOF}, "%s(PANIC=unexpected EOF)"}, - {"%s", PanicF{3}, "%s(PANIC=3)"}, + {"%s", PanicF{io.ErrUnexpectedEOF}, "%!s(PANIC=unexpected EOF)"}, + {"%s", PanicF{3}, "%!s(PANIC=3)"}, } func TestPanics(t *testing.T) { @@ -861,7 +928,7 @@ type Recur struct { failed *bool } -func (r Recur) String() string { +func (r *Recur) String() string { if recurCount++; recurCount > 10 { *r.failed = true return "FAIL" @@ -874,13 +941,13 @@ func (r Recur) String() string { func TestBadVerbRecursion(t *testing.T) { failed := false - r := Recur{3, &failed} + r := &Recur{3, &failed} Sprintf("recur@%p value: %d\n", &r, r.i) if failed { t.Error("fail with pointer") } failed = false - r = Recur{4, &failed} + r = &Recur{4, &failed} Sprintf("recur@%p, value: %d\n", r, r.i) if failed { t.Error("fail with value") diff --git a/libgo/go/fmt/format.go b/libgo/go/fmt/format.go index 5665db1..2e2b071 100644 --- a/libgo/go/fmt/format.go +++ b/libgo/go/fmt/format.go @@ -24,8 +24,6 @@ const ( var padZeroBytes = make([]byte, nByte) var padSpaceBytes = make([]byte, nByte) -var newline = []byte{'\n'} - func init() { for i := 0; i < nByte; i++ { padZeroBytes[i] = '0' @@ -162,6 +160,11 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { } var buf []byte = f.intbuf[0:] + if f.widPresent && f.wid > nByte { + // We're going to need a bigger boat. + buf = make([]byte, f.wid) + } + negative := signedness == signed && a < 0 if negative { a = -a @@ -184,7 +187,7 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { // a is made into unsigned ua. we could make things // marginally faster by splitting the 32-bit case out into a separate // block but it's not worth the duplication, so ua has 64 bits. - i := len(f.intbuf) + i := len(buf) ua := uint64(a) for ua >= base { i-- @@ -193,7 +196,7 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { } i-- buf[i] = digits[ua] - for i > 0 && prec > nByte-i { + for i > 0 && prec > len(buf)-i { i-- buf[i] = '0' } @@ -356,6 +359,14 @@ func (f *fmt) formatFloat(v float64, verb byte, prec, n int) { // 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 + } // We're set; drop the leading space. slice = slice[1:] default: @@ -418,6 +429,8 @@ func (f *fmt) fmt_c64(v complex64, verb rune) { oldPlus := f.plus for i := 0; ; i++ { switch verb { + case 'b': + f.fmt_fb32(r) case 'e': f.fmt_e32(r) case 'E': @@ -446,6 +459,8 @@ func (f *fmt) fmt_c128(v complex128, verb rune) { oldPlus := f.plus for i := 0; ; i++ { switch verb { + case 'b': + f.fmt_fb64(r) case 'e': f.fmt_e64(r) case 'E': diff --git a/libgo/go/fmt/print.go b/libgo/go/fmt/print.go index 5f37fd1..1ea816d 100644 --- a/libgo/go/fmt/print.go +++ b/libgo/go/fmt/print.go @@ -16,19 +16,21 @@ import ( // Some constants in the form of bytes, to avoid string overhead. // Needlessly fastidious, I suppose. var ( - commaSpaceBytes = []byte(", ") - nilAngleBytes = []byte("<nil>") - nilParenBytes = []byte("(nil)") - nilBytes = []byte("nil") - mapBytes = []byte("map[") - missingBytes = []byte("(MISSING)") - panicBytes = []byte("(PANIC=") - extraBytes = []byte("%!(EXTRA ") - irparenBytes = []byte("i)") - bytesBytes = []byte("[]byte{") - badWidthBytes = []byte("%!(BADWIDTH)") - badPrecBytes = []byte("%!(BADPREC)") - noVerbBytes = []byte("%!(NOVERB)") + commaSpaceBytes = []byte(", ") + nilAngleBytes = []byte("<nil>") + nilParenBytes = []byte("(nil)") + nilBytes = []byte("nil") + mapBytes = []byte("map[") + percentBangBytes = []byte("%!") + missingBytes = []byte("(MISSING)") + badIndexBytes = []byte("(BADINDEX)") + panicBytes = []byte("(PANIC=") + extraBytes = []byte("%!(EXTRA ") + irparenBytes = []byte("i)") + bytesBytes = []byte("[]byte{") + badWidthBytes = []byte("%!(BADWIDTH)") + badPrecBytes = []byte("%!(BADPREC)") + noVerbBytes = []byte("%!(NOVERB)") ) // State represents the printer state passed to custom formatters. @@ -42,7 +44,7 @@ type State interface { // Precision returns the value of the precision option and whether it has been set. Precision() (prec int, ok bool) - // Flag returns whether the flag c, a character, has been set. + // Flag reports whether the flag c, a character, has been set. Flag(c int) bool } @@ -109,13 +111,17 @@ type pp struct { panicking bool erroring bool // printing an error condition buf buffer - // field holds the current item, as an interface{}. - field interface{} + // arg holds the current item, as an interface{}. + arg interface{} // value holds the current item, as a reflect.Value, and will be // the zero Value if the item has not been reflected. - value reflect.Value - runeBuf [utf8.UTFMax]byte - fmt fmt + value reflect.Value + // reordered records whether the format string used argument reordering. + reordered bool + // goodArgNum records whether the most recent reordering directive was valid. + goodArgNum bool + runeBuf [utf8.UTFMax]byte + fmt fmt } // A cache holds a set of reusable objects. @@ -170,7 +176,7 @@ func (p *pp) free() { return } p.buf = p.buf[:0] - p.field = nil + p.arg = nil p.value = reflect.Value{} ppFree.put(p) } @@ -212,9 +218,9 @@ func (p *pp) Write(b []byte) (ret int, err error) { func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrintf(format, a) - n64, err := w.Write(p.buf) + n, err = w.Write(p.buf) p.free() - return int(n64), err + return } // Printf formats according to a format specifier and writes to standard output. @@ -246,9 +252,9 @@ func Errorf(format string, a ...interface{}) error { func Fprint(w io.Writer, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrint(a, false, false) - n64, err := w.Write(p.buf) + n, err = w.Write(p.buf) p.free() - return int(n64), err + return } // Print formats using the default formats for its operands and writes to standard output. @@ -278,9 +284,9 @@ func Sprint(a ...interface{}) string { func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { p := newPrinter() p.doPrint(a, true, true) - n64, err := w.Write(p.buf) + n, err = w.Write(p.buf) p.free() - return int(n64), err + return } // Println formats using the default formats for its operands and writes to standard output. @@ -300,8 +306,8 @@ func Sprintln(a ...interface{}) string { return s } -// getField gets the i'th arg of the struct value. -// If the arg itself is an interface, return a value for +// getField gets the i'th field of the struct value. +// If the field is itself is an interface, return a value for // the thing inside the interface, not the interface itself. func getField(v reflect.Value, i int) reflect.Value { val := v.Field(i) @@ -340,10 +346,10 @@ func (p *pp) badVerb(verb rune) { p.add(verb) p.add('(') switch { - case p.field != nil: - p.buf.WriteString(reflect.TypeOf(p.field).String()) + case p.arg != nil: + p.buf.WriteString(reflect.TypeOf(p.arg).String()) p.add('=') - p.printField(p.field, 'v', false, false, 0) + p.printArg(p.arg, 'v', false, false, 0) case p.value.IsValid(): p.buf.WriteString(p.value.Type().String()) p.add('=') @@ -505,7 +511,7 @@ func (p *pp) fmtFloat64(v float64, verb rune) { func (p *pp) fmtComplex64(v complex64, verb rune) { switch verb { - case 'e', 'E', 'f', 'F', 'g', 'G': + case 'b', 'e', 'E', 'f', 'F', 'g', 'G': p.fmt.fmt_c64(v, verb) case 'v': p.fmt.fmt_c64(v, 'g') @@ -516,7 +522,7 @@ func (p *pp) fmtComplex64(v complex64, verb rune) { func (p *pp) fmtComplex128(v complex128, verb rune) { switch verb { - case 'e', 'E', 'f', 'F', 'g', 'G': + case 'b', 'e', 'E', 'f', 'F', 'g', 'G': p.fmt.fmt_c128(v, verb) case 'v': p.fmt.fmt_c128(v, 'g') @@ -566,7 +572,7 @@ func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, typ reflect.Type, dept p.buf.WriteByte(' ') } } - p.printField(c, 'v', p.fmt.plus, goSyntax, depth+1) + p.printArg(c, 'v', p.fmt.plus, goSyntax, depth+1) } if goSyntax { p.buf.WriteByte('}') @@ -635,31 +641,29 @@ func (p *pp) fmtPointer(value reflect.Value, verb rune, goSyntax bool) { var ( intBits = reflect.TypeOf(0).Bits() - floatBits = reflect.TypeOf(0.0).Bits() - complexBits = reflect.TypeOf(1i).Bits() uintptrBits = reflect.TypeOf(uintptr(0)).Bits() ) -func (p *pp) catchPanic(field interface{}, verb rune) { +func (p *pp) catchPanic(arg interface{}, verb rune) { if err := recover(); err != nil { // If it's a nil pointer, just say "<nil>". The likeliest causes are a // Stringer that fails to guard against nil or a nil pointer for a // value receiver, and in either case, "<nil>" is a nice result. - if v := reflect.ValueOf(field); v.Kind() == reflect.Ptr && v.IsNil() { + if v := reflect.ValueOf(arg); v.Kind() == reflect.Ptr && v.IsNil() { p.buf.Write(nilAngleBytes) return } // Otherwise print a concise panic message. Most of the time the panic // value will print itself nicely. if p.panicking { - // Nested panics; the recursion in printField cannot succeed. + // Nested panics; the recursion in printArg cannot succeed. panic(err) } - p.buf.WriteByte('%') + p.buf.Write(percentBangBytes) p.add(verb) p.buf.Write(panicBytes) p.panicking = true - p.printField(err, 'v', false, false, 0) + p.printArg(err, 'v', false, false, 0) p.panicking = false p.buf.WriteByte(')') } @@ -670,10 +674,10 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString return } // Is it a Formatter? - if formatter, ok := p.field.(Formatter); ok { + if formatter, ok := p.arg.(Formatter); ok { handled = true wasString = false - defer p.catchPanic(p.field, verb) + defer p.catchPanic(p.arg, verb) formatter.Format(p, verb) return } @@ -682,13 +686,13 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString p.fmt.plus = false } - // If we're doing Go syntax and the field knows how to supply it, take care of it now. + // If we're doing Go syntax and the argument knows how to supply it, take care of it now. if goSyntax { p.fmt.sharp = false - if stringer, ok := p.field.(GoStringer); ok { + if stringer, ok := p.arg.(GoStringer); ok { wasString = false handled = true - defer p.catchPanic(p.field, verb) + defer p.catchPanic(p.arg, verb) // Print the result of GoString unadorned. p.fmtString(stringer.GoString(), 's', false) return @@ -703,19 +707,19 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString // The duplication in the bodies is necessary: // setting wasString and handled, and deferring catchPanic, // must happen before calling the method. - switch v := p.field.(type) { + switch v := p.arg.(type) { case error: wasString = false handled = true - defer p.catchPanic(p.field, verb) - p.printField(v.Error(), verb, plus, false, depth) + defer p.catchPanic(p.arg, verb) + p.printArg(v.Error(), verb, plus, false, depth) return case Stringer: wasString = false handled = true - defer p.catchPanic(p.field, verb) - p.printField(v.String(), verb, plus, false, depth) + defer p.catchPanic(p.arg, verb) + p.printArg(v.String(), verb, plus, false, depth) return } } @@ -724,11 +728,11 @@ func (p *pp) handleMethods(verb rune, plus, goSyntax bool, depth int) (wasString return } -func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth int) (wasString bool) { - p.field = field +func (p *pp) printArg(arg interface{}, verb rune, plus, goSyntax bool, depth int) (wasString bool) { + p.arg = arg p.value = reflect.Value{} - if field == nil { + if arg == nil { if verb == 'T' || verb == 'v' { p.fmt.pad(nilAngleBytes) } else { @@ -741,10 +745,10 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth // %T (the value's type) and %p (its address) are special; we always do them first. switch verb { case 'T': - p.printField(reflect.TypeOf(field).String(), 's', false, false, 0) + p.printArg(reflect.TypeOf(arg).String(), 's', false, false, 0) return false case 'p': - p.fmtPointer(reflect.ValueOf(field), verb, goSyntax) + p.fmtPointer(reflect.ValueOf(arg), verb, goSyntax) return false } @@ -762,7 +766,7 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth } // Some types can be done without reflection. - switch f := field.(type) { + switch f := arg.(type) { case bool: p.fmtBool(f, verb) case float32: @@ -770,7 +774,7 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth case float64: p.fmtFloat64(f, verb) case complex64: - p.fmtComplex64(complex64(f), verb) + p.fmtComplex64(f, verb) case complex128: p.fmtComplex128(f, verb) case int: @@ -806,17 +810,17 @@ func (p *pp) printField(field interface{}, verb rune, plus, goSyntax bool, depth p.fmt.plus = oldPlus p.fmt.sharp = oldSharp // If the type is not simple, it might have methods. - if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { - return wasString + if isString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { + return isString } // Need to use reflection - return p.printReflectValue(reflect.ValueOf(field), verb, plus, goSyntax, depth) + return p.printReflectValue(reflect.ValueOf(arg), verb, plus, goSyntax, depth) } - p.field = nil + p.arg = nil return } -// printValue is like printField but starts with a reflect value, not an interface{} value. +// printValue is like printArg but starts with a reflect value, not an interface{} value. func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, depth int) (wasString bool) { if !value.IsValid() { if verb == 'T' || verb == 'v' { @@ -831,7 +835,7 @@ func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, dep // %T (the value's type) and %p (its address) are special; we always do them first. switch verb { case 'T': - p.printField(value.Type().String(), 's', false, false, 0) + p.printArg(value.Type().String(), 's', false, false, 0) return false case 'p': p.fmtPointer(value, verb, goSyntax) @@ -839,19 +843,19 @@ func (p *pp) printValue(value reflect.Value, verb rune, plus, goSyntax bool, dep } // Handle values with special methods. - // Call always, even when field == nil, because handleMethods clears p.fmt.plus for us. - p.field = nil // Make sure it's cleared, for safety. + // Call always, even when arg == nil, because handleMethods clears p.fmt.plus for us. + p.arg = nil // Make sure it's cleared, for safety. if value.CanInterface() { - p.field = value.Interface() + p.arg = value.Interface() } - if wasString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { - return wasString + if isString, handled := p.handleMethods(verb, plus, goSyntax, depth); handled { + return isString } return p.printReflectValue(value, verb, plus, goSyntax, depth) } -// printReflectValue is the fallback for both printField and printValue. +// printReflectValue is the fallback for both printArg and printValue. // It uses reflect to print the value. func (p *pp) printReflectValue(value reflect.Value, verb rune, plus, goSyntax bool, depth int) (wasString bool) { oldValue := p.value @@ -863,18 +867,18 @@ BigSwitch: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p.fmtInt64(f.Int(), verb) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - p.fmtUint64(uint64(f.Uint()), verb, goSyntax) + p.fmtUint64(f.Uint(), verb, goSyntax) case reflect.Float32, reflect.Float64: if f.Type().Size() == 4 { p.fmtFloat32(float32(f.Float()), verb) } else { - p.fmtFloat64(float64(f.Float()), verb) + p.fmtFloat64(f.Float(), verb) } case reflect.Complex64, reflect.Complex128: if f.Type().Size() == 8 { p.fmtComplex64(complex64(f.Complex()), verb) } else { - p.fmtComplex128(complex128(f.Complex()), verb) + p.fmtComplex128(f.Complex(), verb) } case reflect.String: p.fmtString(f.String(), verb, goSyntax) @@ -1015,20 +1019,59 @@ BigSwitch: return wasString } -// intFromArg gets the fieldnumth element of a. On return, isInt reports whether the argument has type int. -func intFromArg(a []interface{}, end, i, fieldnum int) (num int, isInt bool, newi, newfieldnum int) { - newi, newfieldnum = end, fieldnum - if i < end && fieldnum < len(a) { - num, isInt = a[fieldnum].(int) - newi, newfieldnum = i+1, fieldnum+1 +// intFromArg gets the argNumth element of a. On return, isInt reports whether the argument has type int. +func intFromArg(a []interface{}, argNum int) (num int, isInt bool, newArgNum int) { + newArgNum = argNum + if argNum < len(a) { + num, isInt = a[argNum].(int) + newArgNum = argNum + 1 } return } +// parseArgNumber returns the value of the bracketed number, minus 1 +// (explicit argument numbers are one-indexed but we want zero-indexed). +// The opening bracket is known to be present at format[0]. +// The returned values are the index, the number of bytes to consume +// up to the closing paren, if present, and whether the number parsed +// ok. The bytes to consume will be 1 if no closing paren is present. +func parseArgNumber(format string) (index int, wid int, ok bool) { + // Find closing bracket. + for i := 1; i < len(format); i++ { + if format[i] == ']' { + width, ok, newi := parsenum(format, 1, i) + if !ok || newi != i { + return 0, i + 1, false + } + return width - 1, i + 1, true // arg numbers are one-indexed and skip paren. + } + } + return 0, 1, false +} + +// argNumber returns the next argument to evaluate, which is either the value of the passed-in +// argNum or the value of the bracketed integer that begins format[i:]. It also returns +// the new value of i, that is, the index of the next byte of the format to process. +func (p *pp) argNumber(argNum int, format string, i int, numArgs int) (newArgNum, newi int, found bool) { + if len(format) <= i || format[i] != '[' { + return argNum, i, false + } + p.reordered = true + index, wid, ok := parseArgNumber(format[i:]) + if ok && 0 <= index && index < numArgs { + return index, i + wid, true + } + p.goodArgNum = false + return argNum, i + wid, true +} + func (p *pp) doPrintf(format string, a []interface{}) { end := len(format) - fieldnum := 0 // we process one field per non-trivial format + argNum := 0 // we process one argument per non-trivial format + afterIndex := false // previous item in format was an index like [3]. + p.reordered = false for i := 0; i < end; { + p.goodArgNum = true lasti := i for i < end && format[i] != '%' { i++ @@ -1043,7 +1086,8 @@ func (p *pp) doPrintf(format string, a []interface{}) { // Process one verb i++ - // flags and widths + + // Do we have flags? p.fmt.clearflags() F: for ; i < end; i++ { @@ -1062,30 +1106,52 @@ func (p *pp) doPrintf(format string, a []interface{}) { break F } } - // do we have width? + + // Do we have an explicit argument index? + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + + // Do we have width? if i < end && format[i] == '*' { - p.fmt.wid, p.fmt.widPresent, i, fieldnum = intFromArg(a, end, i, fieldnum) + i++ + p.fmt.wid, p.fmt.widPresent, argNum = intFromArg(a, argNum) if !p.fmt.widPresent { p.buf.Write(badWidthBytes) } + afterIndex = false } else { p.fmt.wid, p.fmt.widPresent, i = parsenum(format, i, end) + if afterIndex && p.fmt.widPresent { // "%[3]2d" + p.goodArgNum = false + } } - // do we have precision? + + // Do we have precision? if i+1 < end && format[i] == '.' { - if format[i+1] == '*' { - p.fmt.prec, p.fmt.precPresent, i, fieldnum = intFromArg(a, end, i+1, fieldnum) + i++ + if afterIndex { // "%[3].2d" + p.goodArgNum = false + } + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + if format[i] == '*' { + i++ + p.fmt.prec, p.fmt.precPresent, argNum = intFromArg(a, argNum) if !p.fmt.precPresent { p.buf.Write(badPrecBytes) } + afterIndex = false } else { - p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i+1, end) + p.fmt.prec, p.fmt.precPresent, i = parsenum(format, i, end) if !p.fmt.precPresent { p.fmt.prec = 0 p.fmt.precPresent = true } } } + + if !afterIndex { + argNum, i, afterIndex = p.argNumber(argNum, format, i, len(a)) + } + if i >= end { p.buf.Write(noVerbBytes) continue @@ -1097,30 +1163,38 @@ func (p *pp) doPrintf(format string, a []interface{}) { p.buf.WriteByte('%') // We ignore width and prec. continue } - if fieldnum >= len(a) { // out of operands - p.buf.WriteByte('%') + if !p.goodArgNum { + p.buf.Write(percentBangBytes) + p.add(c) + p.buf.Write(badIndexBytes) + continue + } else if argNum >= len(a) { // out of operands + p.buf.Write(percentBangBytes) p.add(c) p.buf.Write(missingBytes) continue } - field := a[fieldnum] - fieldnum++ + arg := a[argNum] + argNum++ goSyntax := c == 'v' && p.fmt.sharp plus := c == 'v' && p.fmt.plus - p.printField(field, c, plus, goSyntax, 0) + p.printArg(arg, c, plus, goSyntax, 0) } - if fieldnum < len(a) { + // Check for extra arguments unless the call accessed the arguments + // out of order, in which case it's too expensive to detect if they've all + // been used and arguably OK if they're not. + if !p.reordered && argNum < len(a) { p.buf.Write(extraBytes) - for ; fieldnum < len(a); fieldnum++ { - field := a[fieldnum] - if field != nil { - p.buf.WriteString(reflect.TypeOf(field).String()) + for ; argNum < len(a); argNum++ { + arg := a[argNum] + if arg != nil { + p.buf.WriteString(reflect.TypeOf(arg).String()) p.buf.WriteByte('=') } - p.printField(field, 'v', false, false, 0) - if fieldnum+1 < len(a) { + p.printArg(arg, 'v', false, false, 0) + if argNum+1 < len(a) { p.buf.Write(commaSpaceBytes) } } @@ -1130,17 +1204,17 @@ func (p *pp) doPrintf(format string, a []interface{}) { func (p *pp) doPrint(a []interface{}, addspace, addnewline bool) { prevString := false - for fieldnum := 0; fieldnum < len(a); fieldnum++ { + for argNum := 0; argNum < len(a); argNum++ { p.fmt.clearflags() // always add spaces if we're doing Println - field := a[fieldnum] - if fieldnum > 0 { - isString := field != nil && reflect.TypeOf(field).Kind() == reflect.String + arg := a[argNum] + if argNum > 0 { + isString := arg != nil && reflect.TypeOf(arg).Kind() == reflect.String if addspace || !isString && !prevString { p.buf.WriteByte(' ') } } - prevString = p.printField(field, 'v', false, false, 0) + prevString = p.printArg(arg, 'v', false, false, 0) } if addnewline { p.buf.WriteByte('\n') diff --git a/libgo/go/fmt/scan.go b/libgo/go/fmt/scan.go index bf888c4..5b1be58 100644 --- a/libgo/go/fmt/scan.go +++ b/libgo/go/fmt/scan.go @@ -168,12 +168,12 @@ type ss struct { // ssave holds the parts of ss that need to be // saved and restored on recursive scans. type ssave struct { - validSave bool // is or was a part of an actual ss. - nlIsEnd bool // whether newline terminates scan - nlIsSpace bool // whether newline counts as white space - fieldLimit int // max value of ss.count for this field; fieldLimit <= limit - limit int // max value of ss.count. - maxWid int // width of this field. + validSave bool // is or was a part of an actual ss. + nlIsEnd bool // whether newline terminates scan + nlIsSpace bool // whether newline counts as white space + argLimit int // max value of ss.count for this arg; argLimit <= limit + limit int // max value of ss.count. + maxWid int // width of this arg. } // The Read method is only in ScanState so that ScanState @@ -192,7 +192,7 @@ func (s *ss) ReadRune() (r rune, size int, err error) { s.peekRune = -1 return } - if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.fieldLimit { + if s.atEOF || s.nlIsEnd && s.prevRune == '\n' || s.count >= s.argLimit { err = io.EOF return } @@ -389,7 +389,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { s, ok := r.(*ss) if ok { old = s.ssave - s.limit = s.fieldLimit + s.limit = s.argLimit s.nlIsEnd = nlIsEnd || s.nlIsEnd s.nlIsSpace = nlIsSpace return @@ -407,7 +407,7 @@ func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) (s *ss, old ssave) { s.peekRune = -1 s.atEOF = false s.limit = hugeWid - s.fieldLimit = hugeWid + s.argLimit = hugeWid s.maxWid = hugeWid s.validSave = true s.count = 0 @@ -437,6 +437,9 @@ func (s *ss) skipSpace(stopAtNewline bool) { if r == eof { return } + if r == '\r' && s.peek("\n") { + continue + } if r == '\n' { if stopAtNewline { break @@ -476,11 +479,6 @@ func (s *ss) token(skipSpace bool, f func(rune) bool) []byte { return s.buf } -// typeError indicates that the type of the operand did not match the format -func (s *ss) typeError(field interface{}, expected string) { - s.errorString("expected field of type pointer to " + expected + "; found " + reflect.TypeOf(field).String()) -} - var complexError = errors.New("syntax error scanning complex number") var boolError = errors.New("syntax error scanning boolean") @@ -781,7 +779,7 @@ func (s *ss) convertFloat(str string, n int) float64 { } s.error(err) } - n, err := strconv.Atoi(str[p+1:]) + m, err := strconv.Atoi(str[p+1:]) if err != nil { // Put full string into error. if e, ok := err.(*strconv.NumError); ok { @@ -789,7 +787,7 @@ func (s *ss) convertFloat(str string, n int) float64 { } s.error(err) } - return math.Ldexp(f, n) + return math.Ldexp(f, m) } f, err := strconv.ParseFloat(str, n) if err != nil { @@ -858,8 +856,7 @@ func (s *ss) quotedString() string { // In a legal backslash escape, no matter how long, only the character // immediately after the escape can itself be a backslash or quote. // Thus we only need to protect the first character after the backslash. - r := s.mustReadRune() - s.buf.WriteRune(r) + s.buf.WriteRune(s.mustReadRune()) } else if r == '"' { break } @@ -886,7 +883,7 @@ func (s *ss) hexDigit(d rune) int { case 'A', 'B', 'C', 'D', 'E', 'F': return 10 + digit - 'A' } - s.errorString("Scan: illegal hex digit") + s.errorString("illegal hex digit") return 0 } @@ -916,7 +913,7 @@ func (s *ss) hexString() string { s.buf.WriteByte(b) } if len(s.buf) == 0 { - s.errorString("Scan: no hex data for %x string") + s.errorString("no hex data for %x string") return "" } return string(s.buf) @@ -927,11 +924,11 @@ const floatVerbs = "beEfFgGv" const hugeWid = 1 << 30 // scanOne scans a single value, deriving the scanner from the type of the argument. -func (s *ss) scanOne(verb rune, field interface{}) { +func (s *ss) scanOne(verb rune, arg interface{}) { s.buf = s.buf[:0] var err error // If the parameter has its own Scan method, use that. - if v, ok := field.(Scanner); ok { + if v, ok := arg.(Scanner); ok { err = v.Scan(s, verb) if err != nil { if err == io.EOF { @@ -942,7 +939,7 @@ func (s *ss) scanOne(verb rune, field interface{}) { return } - switch v := field.(type) { + switch v := arg.(type) { case *bool: *v = s.scanBool(verb) case *complex64: @@ -995,7 +992,7 @@ func (s *ss) scanOne(verb rune, field interface{}) { val := reflect.ValueOf(v) ptr := val if ptr.Kind() != reflect.Ptr { - s.errorString("Scan: type not a pointer: " + val.Type().String()) + s.errorString("type not a pointer: " + val.Type().String()) return } switch v := ptr.Elem(); v.Kind() { @@ -1011,7 +1008,7 @@ func (s *ss) scanOne(verb rune, field interface{}) { // For now, can only handle (renamed) []byte. typ := v.Type() if typ.Elem().Kind() != reflect.Uint8 { - s.errorString("Scan: can't handle type: " + val.Type().String()) + s.errorString("can't scan type: " + val.Type().String()) } str := s.convertString(verb) v.Set(reflect.MakeSlice(typ, len(str), len(str))) @@ -1025,7 +1022,7 @@ func (s *ss) scanOne(verb rune, field interface{}) { case reflect.Complex64, reflect.Complex128: v.SetComplex(s.scanComplex(verb, v.Type().Bits())) default: - s.errorString("Scan: can't handle type: " + val.Type().String()) + s.errorString("can't scan type: " + val.Type().String()) } } } @@ -1046,8 +1043,8 @@ func errorHandler(errp *error) { // doScan does the real work for scanning without a format string. func (s *ss) doScan(a []interface{}) (numProcessed int, err error) { defer errorHandler(&err) - for _, field := range a { - s.scanOne('v', field) + for _, arg := range a { + s.scanOne('v', arg) numProcessed++ } // Check for newline if required. @@ -1058,7 +1055,7 @@ func (s *ss) doScan(a []interface{}) (numProcessed int, err error) { break } if !isSpace(r) { - s.errorString("Scan: expected newline") + s.errorString("expected newline") break } } @@ -1144,9 +1141,9 @@ func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err erro if !widPresent { s.maxWid = hugeWid } - s.fieldLimit = s.limit - if f := s.count + s.maxWid; f < s.fieldLimit { - s.fieldLimit = f + s.argLimit = s.limit + if f := s.count + s.maxWid; f < s.argLimit { + s.argLimit = f } c, w := utf8.DecodeRuneInString(format[i:]) @@ -1156,11 +1153,11 @@ func (s *ss) doScanf(format string, a []interface{}) (numProcessed int, err erro s.errorString("too few operands for format %" + format[i-w:]) break } - field := a[numProcessed] + arg := a[numProcessed] - s.scanOne(c, field) + s.scanOne(c, arg) numProcessed++ - s.fieldLimit = s.limit + s.argLimit = s.limit } if numProcessed < len(a) { s.errorString("too many operands") diff --git a/libgo/go/fmt/scan_test.go b/libgo/go/fmt/scan_test.go index 4e2c0fe..d903f0c 100644 --- a/libgo/go/fmt/scan_test.go +++ b/libgo/go/fmt/scan_test.go @@ -54,7 +54,6 @@ var ( float32Val float32 float64Val float64 stringVal string - stringVal1 string bytesVal []byte runeVal rune complex64Val complex64 @@ -192,6 +191,10 @@ var scanTests = []ScanTest{ {"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)}, {"hello\n", &stringVal, "hello"}, + // Carriage-return followed by newline. (We treat \r\n as \n always.) + {"hello\r\n", &stringVal, "hello"}, + {"27\r\n", &uint8Val, uint8(27)}, + // Renamed types {"true\n", &renamedBoolVal, renamedBool(true)}, {"F\n", &renamedBoolVal, renamedBool(false)}, diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go index bf533d1..6e635cd 100644 --- a/libgo/go/go/ast/ast.go +++ b/libgo/go/go/ast/ast.go @@ -297,6 +297,8 @@ type ( Lbrack token.Pos // position of "[" Low Expr // begin of slice range; or nil High Expr // end of slice range; or nil + Max Expr // maximum capacity of slice; or nil + Slice3 bool // true if 3-index slice (2 colons present) Rbrack token.Pos // position of "]" } @@ -304,8 +306,10 @@ type ( // type assertion. // TypeAssertExpr struct { - X Expr // expression - Type Expr // asserted type; nil means type switch X.(type) + X Expr // expression + Lparen token.Pos // position of "(" + Type Expr // asserted type; nil means type switch X.(type) + Rparen token.Pos // position of ")" } // A CallExpr node represents an expression followed by an argument list. @@ -385,8 +389,8 @@ type ( // A FuncType node represents a function type. FuncType struct { - Func token.Pos // position of "func" keyword - Params *FieldList // (incoming) parameters; or nil + Func token.Pos // position of "func" keyword (token.NoPos if there is no "func") + Params *FieldList // (incoming) parameters; non-nil Results *FieldList // (outgoing) results; or nil } @@ -407,7 +411,7 @@ type ( // A ChanType node represents a channel type. ChanType struct { Begin token.Pos // position of "chan" keyword or "<-" (whichever comes first) - Arrow token.Pos // position of "<-" (noPos if there is no "<-") + Arrow token.Pos // position of "<-" (token.NoPos if there is no "<-") Dir ChanDir // channel direction Value Expr // value type } @@ -438,10 +442,15 @@ func (x *BinaryExpr) Pos() token.Pos { return x.X.Pos() } func (x *KeyValueExpr) Pos() token.Pos { return x.Key.Pos() } func (x *ArrayType) Pos() token.Pos { return x.Lbrack } func (x *StructType) Pos() token.Pos { return x.Struct } -func (x *FuncType) Pos() token.Pos { return x.Func } -func (x *InterfaceType) Pos() token.Pos { return x.Interface } -func (x *MapType) Pos() token.Pos { return x.Map } -func (x *ChanType) Pos() token.Pos { return x.Begin } +func (x *FuncType) Pos() token.Pos { + if x.Func.IsValid() || x.Params == nil { // see issue 3870 + return x.Func + } + return x.Params.Pos() // interface method declarations have no "func" keyword +} +func (x *InterfaceType) Pos() token.Pos { return x.Interface } +func (x *MapType) Pos() token.Pos { return x.Map } +func (x *ChanType) Pos() token.Pos { return x.Begin } func (x *BadExpr) End() token.Pos { return x.To } func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) } @@ -451,26 +460,21 @@ func (x *Ellipsis) End() token.Pos { } return x.Ellipsis + 3 // len("...") } -func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) } -func (x *FuncLit) End() token.Pos { return x.Body.End() } -func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 } -func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 } -func (x *SelectorExpr) End() token.Pos { return x.Sel.End() } -func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 } -func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 } -func (x *TypeAssertExpr) End() token.Pos { - if x.Type != nil { - return x.Type.End() - } - return x.X.End() -} -func (x *CallExpr) End() token.Pos { return x.Rparen + 1 } -func (x *StarExpr) End() token.Pos { return x.X.End() } -func (x *UnaryExpr) End() token.Pos { return x.X.End() } -func (x *BinaryExpr) End() token.Pos { return x.Y.End() } -func (x *KeyValueExpr) End() token.Pos { return x.Value.End() } -func (x *ArrayType) End() token.Pos { return x.Elt.End() } -func (x *StructType) End() token.Pos { return x.Fields.End() } +func (x *BasicLit) End() token.Pos { return token.Pos(int(x.ValuePos) + len(x.Value)) } +func (x *FuncLit) End() token.Pos { return x.Body.End() } +func (x *CompositeLit) End() token.Pos { return x.Rbrace + 1 } +func (x *ParenExpr) End() token.Pos { return x.Rparen + 1 } +func (x *SelectorExpr) End() token.Pos { return x.Sel.End() } +func (x *IndexExpr) End() token.Pos { return x.Rbrack + 1 } +func (x *SliceExpr) End() token.Pos { return x.Rbrack + 1 } +func (x *TypeAssertExpr) End() token.Pos { return x.Rparen + 1 } +func (x *CallExpr) End() token.Pos { return x.Rparen + 1 } +func (x *StarExpr) End() token.Pos { return x.X.End() } +func (x *UnaryExpr) End() token.Pos { return x.X.End() } +func (x *BinaryExpr) End() token.Pos { return x.Y.End() } +func (x *KeyValueExpr) End() token.Pos { return x.Value.End() } +func (x *ArrayType) End() token.Pos { return x.Elt.End() } +func (x *StructType) End() token.Pos { return x.Fields.End() } func (x *FuncType) End() token.Pos { if x.Results != nil { return x.Results.End() @@ -511,23 +515,21 @@ func (*ChanType) exprNode() {} // ---------------------------------------------------------------------------- // Convenience functions for Idents -var noPos token.Pos - // NewIdent creates a new Ident without position. // Useful for ASTs generated by code other than the Go parser. // -func NewIdent(name string) *Ident { return &Ident{noPos, name, nil} } +func NewIdent(name string) *Ident { return &Ident{token.NoPos, name, nil} } -// IsExported returns whether name is an exported Go symbol -// (i.e., whether it begins with an uppercase letter). +// IsExported reports whether name is an exported Go symbol +// (that is, whether it begins with an upper-case letter). // func IsExported(name string) bool { ch, _ := utf8.DecodeRuneInString(name) return unicode.IsUpper(ch) } -// IsExported returns whether id is an exported Go symbol -// (i.e., whether it begins with an uppercase letter). +// IsExported reports whether id is an exported Go symbol +// (that is, whether it begins with an uppercase letter). // func (id *Ident) IsExported() bool { return IsExported(id.Name) } @@ -919,7 +921,7 @@ type ( Doc *CommentGroup // associated documentation; or nil Recv *FieldList // receiver (methods); or nil (functions) Name *Ident // function/method name - Type *FuncType // position of Func keyword, parameters and results + Type *FuncType // function signature: parameters, results, and position of "func" keyword Body *BlockStmt // function body; or nil (forward declaration) } ) diff --git a/libgo/go/go/ast/commentmap.go b/libgo/go/go/ast/commentmap.go index 252d460..1fb4867 100644 --- a/libgo/go/go/ast/commentmap.go +++ b/libgo/go/go/ast/commentmap.go @@ -129,11 +129,11 @@ func (s *nodeStack) pop(pos token.Pos) (top Node) { // // A comment group g is associated with a node n if: // -// - g starts on the same line as n ends -// - g starts on the line immediately following n, and there is -// at least one empty line after g and before the next node -// - g starts before n and is not associated to the node before n -// via the previous rules +// - g starts on the same line as n ends +// - g starts on the line immediately following n, and there is +// at least one empty line after g and before the next node +// - g starts before n and is not associated to the node before n +// via the previous rules // // NewCommentMap tries to associate a comment group to the "largest" // node possible: For instance, if the comment is a line comment diff --git a/libgo/go/go/ast/filter.go b/libgo/go/go/ast/filter.go index 71c9ed7..fc3eeb4 100644 --- a/libgo/go/go/ast/filter.go +++ b/libgo/go/go/ast/filter.go @@ -308,7 +308,7 @@ func nameOf(f *FuncDecl) string { // separator is an empty //-style comment that is interspersed between // different comment groups when they are concatenated into a single group // -var separator = &Comment{noPos, "//"} +var separator = &Comment{token.NoPos, "//"} // MergePackageFiles creates a file AST by merging the ASTs of the // files belonging to a package. The mode flags control merging behavior. diff --git a/libgo/go/go/ast/import.go b/libgo/go/go/ast/import.go index a68a484..d2770d1 100644 --- a/libgo/go/go/ast/import.go +++ b/libgo/go/go/ast/import.go @@ -11,6 +11,7 @@ import ( ) // SortImports sorts runs of consecutive import lines in import blocks in f. +// It also removes duplicate imports when it is possible to do so without data loss. func SortImports(fset *token.FileSet, f *File) { for _, d := range f.Decls { d, ok := d.(*GenDecl) @@ -27,14 +28,25 @@ func SortImports(fset *token.FileSet, f *File) { // Identify and sort runs of specs on successive lines. i := 0 + specs := d.Specs[:0] for j, s := range d.Specs { if j > i && fset.Position(s.Pos()).Line > 1+fset.Position(d.Specs[j-1].End()).Line { // j begins a new run. End this one. - sortSpecs(fset, f, d.Specs[i:j]) + specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...) i = j } } - sortSpecs(fset, f, d.Specs[i:]) + specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...) + d.Specs = specs + + // Deduping can leave a blank line before the rparen; clean that up. + if len(d.Specs) > 0 { + lastSpec := d.Specs[len(d.Specs)-1] + lastLine := fset.Position(lastSpec.Pos()).Line + if rParenLine := fset.Position(d.Rparen).Line; rParenLine > lastLine+1 { + fset.File(d.Rparen).MergeLine(rParenLine - 1) + } + } } } @@ -46,22 +58,41 @@ func importPath(s Spec) string { return "" } +func importName(s Spec) string { + n := s.(*ImportSpec).Name + if n == nil { + return "" + } + return n.Name +} + +func importComment(s Spec) string { + c := s.(*ImportSpec).Comment + if c == nil { + return "" + } + return c.Text() +} + +// collapse indicates whether prev may be removed, leaving only next. +func collapse(prev, next Spec) bool { + if importPath(next) != importPath(prev) || importName(next) != importName(prev) { + return false + } + return prev.(*ImportSpec).Comment == nil +} + type posSpan struct { Start token.Pos End token.Pos } -func sortSpecs(fset *token.FileSet, f *File, specs []Spec) { - // Avoid work if already sorted (also catches < 2 entries). - sorted := true - for i, s := range specs { - if i > 0 && importPath(specs[i-1]) > importPath(s) { - sorted = false - break - } - } - if sorted { - return +func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec { + // Can't short-circuit here even if specs are already sorted, + // since they might yet need deduplication. + // A lone import, however, may be safely ignored. + if len(specs) <= 1 { + return specs } // Record positions for specs. @@ -101,10 +132,26 @@ func sortSpecs(fset *token.FileSet, f *File, specs []Spec) { } // Sort the import specs by import path. + // Remove duplicates, when possible without data loss. // Reassign the import paths to have the same position sequence. // Reassign each comment to abut the end of its spec. // Sort the comments by new position. - sort.Sort(byImportPath(specs)) + sort.Sort(byImportSpec(specs)) + + // Dedup. Thanks to our sorting, we can just consider + // adjacent pairs of imports. + deduped := specs[:0] + for i, s := range specs { + if i == len(specs)-1 || !collapse(s, specs[i+1]) { + deduped = append(deduped, s) + } else { + p := s.Pos() + fset.File(p).MergeLine(fset.Position(p).Line) + } + } + specs = deduped + + // Fix up comment positions for i, s := range specs { s := s.(*ImportSpec) if s.Name != nil { @@ -118,14 +165,29 @@ func sortSpecs(fset *token.FileSet, f *File, specs []Spec) { } } } + sort.Sort(byCommentPos(comments)) + + return specs } -type byImportPath []Spec // slice of *ImportSpec +type byImportSpec []Spec // slice of *ImportSpec -func (x byImportPath) Len() int { return len(x) } -func (x byImportPath) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x byImportPath) Less(i, j int) bool { return importPath(x[i]) < importPath(x[j]) } +func (x byImportSpec) Len() int { return len(x) } +func (x byImportSpec) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x byImportSpec) Less(i, j int) bool { + ipath := importPath(x[i]) + jpath := importPath(x[j]) + if ipath != jpath { + return ipath < jpath + } + iname := importName(x[i]) + jname := importName(x[j]) + if iname != jname { + return iname < jname + } + return importComment(x[i]) < importComment(x[j]) +} type byCommentPos []*CommentGroup diff --git a/libgo/go/go/ast/walk.go b/libgo/go/go/ast/walk.go index fef2503..fedffb3 100644 --- a/libgo/go/go/ast/walk.go +++ b/libgo/go/go/ast/walk.go @@ -122,6 +122,9 @@ func Walk(v Visitor, node Node) { if n.High != nil { Walk(v, n.High) } + if n.Max != nil { + Walk(v, n.Max) + } case *TypeAssertExpr: Walk(v, n.X) diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go index cc89afb..50d2fb4 100644 --- a/libgo/go/go/build/build.go +++ b/libgo/go/go/build/build.go @@ -258,21 +258,23 @@ func (ctxt *Context) SrcDirs() []string { var Default Context = defaultContext() var cgoEnabled = map[string]bool{ - "darwin/386": true, - "darwin/amd64": true, - "freebsd/386": true, - "freebsd/amd64": true, - "freebsd/arm": true, - "linux/386": true, - "linux/amd64": true, - "linux/arm": true, - "netbsd/386": true, - "netbsd/amd64": true, - "netbsd/arm": true, - "openbsd/386": true, - "openbsd/amd64": true, - "windows/386": true, - "windows/amd64": true, + "darwin/386": true, + "darwin/amd64": true, + "dragonfly/386": true, + "dragonfly/amd64": true, + "freebsd/386": true, + "freebsd/amd64": true, + "freebsd/arm": true, + "linux/386": true, + "linux/amd64": true, + "linux/arm": true, + "netbsd/386": true, + "netbsd/amd64": true, + "netbsd/arm": true, + "openbsd/386": true, + "openbsd/amd64": true, + "windows/386": true, + "windows/amd64": true, } func defaultContext() Context { @@ -293,7 +295,7 @@ func defaultContext() Context { // When we reach Go 1.3 the line will read // c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3"} // and so on. - c.ReleaseTags = []string{"go1.1"} + c.ReleaseTags = []string{"go1.1", "go1.2"} switch os.Getenv("CGO_ENABLED") { case "1": @@ -337,32 +339,37 @@ const ( // A Package describes the Go package found in a directory. type Package struct { - Dir string // directory containing package sources - Name string // package name - Doc string // documentation synopsis - ImportPath string // import path of package ("" if unknown) - Root string // root of Go tree where this package lives - SrcRoot string // package source root directory ("" if unknown) - PkgRoot string // package install root directory ("" if unknown) - BinDir string // command install directory ("" if unknown) - Goroot bool // package found in Go root - PkgObj string // installed .a file + Dir string // directory containing package sources + Name string // package name + Doc string // documentation synopsis + ImportPath string // import path of package ("" if unknown) + Root string // root of Go tree where this package lives + SrcRoot string // package source root directory ("" if unknown) + PkgRoot string // package install root directory ("" if unknown) + BinDir string // command install directory ("" if unknown) + Goroot bool // package found in Go root + PkgObj string // installed .a file + AllTags []string // tags that can influence file selection in this directory + ConflictDir string // this directory shadows Dir in $GOPATH // Source files GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) CgoFiles []string // .go source files that import "C" IgnoredGoFiles []string // .go source files ignored for this build CFiles []string // .c source files - HFiles []string // .h source files + CXXFiles []string // .cc, .cpp and .cxx source files + HFiles []string // .h, .hh, .hpp and .hxx source files SFiles []string // .s source files - SysoFiles []string // .syso system object files to add to archive SwigFiles []string // .swig files SwigCXXFiles []string // .swigcxx files + SysoFiles []string // .syso system object files to add to archive // Cgo directives - CgoPkgConfig []string // Cgo pkg-config directives CgoCFLAGS []string // Cgo CFLAGS directives + CgoCPPFLAGS []string // Cgo CPPFLAGS directives + CgoCXXFLAGS []string // Cgo CXXFLAGS directives CgoLDFLAGS []string // Cgo LDFLAGS directives + CgoPkgConfig []string // Cgo pkg-config directives // Dependency information Imports []string // imports from GoFiles, CgoFiles @@ -391,13 +398,22 @@ func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) { } // NoGoError is the error used by Import to describe a directory -// containing no Go source files. +// containing no buildable Go source files. (It may still contain +// test files, files hidden by build tags, and so on.) type NoGoError struct { Dir string } func (e *NoGoError) Error() string { - return "no Go source files in " + e.Dir + return "no buildable Go source files in " + e.Dir +} + +func nameExt(name string) string { + i := strings.LastIndex(name, ".") + if i < 0 { + return "" + } + return name[i:] } // Import returns details about the Go package named by the import path, @@ -429,7 +445,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa switch ctxt.Compiler { case "gccgo": dir, elem := pathpkg.Split(p.ImportPath) - pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a" + pkga = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + dir + "lib" + elem + ".a" case "gc": suffix := "" if ctxt.InstallSuffix != "" { @@ -469,11 +485,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa // else first. if ctxt.GOROOT != "" { if dir := ctxt.joinPath(ctxt.GOROOT, "src", "pkg", sub); ctxt.isDir(dir) { + p.ConflictDir = dir goto Found } } for _, earlyRoot := range all[:i] { if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) { + p.ConflictDir = dir goto Found } } @@ -575,63 +593,21 @@ Found: imported := make(map[string][]token.Position) testImported := make(map[string][]token.Position) xTestImported := make(map[string][]token.Position) + allTags := make(map[string]bool) fset := token.NewFileSet() for _, d := range dirs { if d.IsDir() { continue } - name := d.Name() - if strings.HasPrefix(name, "_") || - strings.HasPrefix(name, ".") { - continue - } - - i := strings.LastIndex(name, ".") - if i < 0 { - i = len(name) - } - ext := name[i:] - - if !ctxt.UseAllFiles && !ctxt.goodOSArchFile(name) { - if ext == ".go" { - p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) - } - continue - } - switch ext { - case ".go", ".c", ".s", ".h", ".S", ".swig", ".swigcxx": - // tentatively okay - read to make sure - case ".syso": - // binary objects to add to package archive - // Likely of the form foo_windows.syso, but - // the name was vetted above with goodOSArchFile. - p.SysoFiles = append(p.SysoFiles, name) - continue - default: - // skip - continue - } + name := d.Name() + ext := nameExt(name) - filename := ctxt.joinPath(p.Dir, name) - f, err := ctxt.openFile(filename) + match, data, filename, err := ctxt.matchFile(p.Dir, name, true, allTags) if err != nil { return p, err } - - var data []byte - if strings.HasSuffix(filename, ".go") { - data, err = readImports(f, false) - } else { - data, err = readComments(f) - } - f.Close() - if err != nil { - return p, fmt.Errorf("read %s: %v", filename, err) - } - - // Look for +build comments to accept or reject the file. - if !ctxt.UseAllFiles && !ctxt.shouldBuild(data) { + if !match { if ext == ".go" { p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) } @@ -643,7 +619,10 @@ Found: case ".c": p.CFiles = append(p.CFiles, name) continue - case ".h": + case ".cc", ".cpp", ".cxx": + p.CXXFiles = append(p.CXXFiles, name) + continue + case ".h", ".hh", ".hpp", ".hxx": p.HFiles = append(p.HFiles, name) continue case ".s": @@ -658,6 +637,12 @@ Found: case ".swigcxx": p.SwigCXXFiles = append(p.SwigCXXFiles, name) continue + case ".syso": + // binary objects to add to package archive + // Likely of the form foo_windows.syso, but + // the name was vetted above with goodOSArchFile. + p.SysoFiles = append(p.SysoFiles, name) + continue } pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments) @@ -730,8 +715,11 @@ Found: } } if isCgo { + allTags["cgo"] = true if ctxt.CgoEnabled { p.CgoFiles = append(p.CgoFiles, name) + } else { + p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) } } else if isXTest { p.XTestGoFiles = append(p.XTestGoFiles, name) @@ -741,10 +729,15 @@ Found: p.GoFiles = append(p.GoFiles, name) } } - if p.Name == "" { + if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { return p, &NoGoError{p.Dir} } + for tag := range allTags { + p.AllTags = append(p.AllTags, tag) + } + sort.Strings(p.AllTags) + p.Imports, p.ImportPos = cleanImports(imported) p.TestImports, p.TestImportPos = cleanImports(testImported) p.XTestImports, p.XTestImportPos = cleanImports(xTestImported) @@ -760,6 +753,79 @@ Found: return p, pkgerr } +// MatchFile reports whether the file with the given name in the given directory +// matches the context and would be included in a Package created by ImportDir +// of that directory. +// +// MatchFile considers the name of the file and may use ctxt.OpenFile to +// read some or all of the file's content. +func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) { + match, _, _, err = ctxt.matchFile(dir, name, false, nil) + return +} + +// matchFile determines whether the file with the given name in the given directory +// should be included in the package being constructed. +// It returns the data read from the file. +// If returnImports is true and name denotes a Go program, matchFile reads +// until the end of the imports (and returns that data) even though it only +// considers text until the first non-comment. +// If allTags is non-nil, matchFile records any encountered build tag +// by setting allTags[tag] = true. +func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool) (match bool, data []byte, filename string, err error) { + if strings.HasPrefix(name, "_") || + strings.HasPrefix(name, ".") { + return + } + + i := strings.LastIndex(name, ".") + if i < 0 { + i = len(name) + } + ext := name[i:] + + if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles { + return + } + + switch ext { + case ".go", ".c", ".cc", ".cxx", ".cpp", ".s", ".h", ".hh", ".hpp", ".hxx", ".S", ".swig", ".swigcxx": + // tentatively okay - read to make sure + case ".syso": + // binary, no reading + match = true + return + default: + // skip + return + } + + filename = ctxt.joinPath(dir, name) + f, err := ctxt.openFile(filename) + if err != nil { + return + } + + if strings.HasSuffix(filename, ".go") { + data, err = readImports(f, false) + } else { + data, err = readComments(f) + } + f.Close() + if err != nil { + err = fmt.Errorf("read %s: %v", filename, err) + return + } + + // Look for +build comments to accept or reject the file. + if !ctxt.shouldBuild(data, allTags) && !ctxt.UseAllFiles { + return + } + + match = true + return +} + func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) { all := make([]string, 0, len(m)) for path := range m { @@ -794,7 +860,7 @@ var slashslash = []byte("//") // // marks the file as applicable only on Windows and Linux. // -func (ctxt *Context) shouldBuild(content []byte) bool { +func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) bool { // Pass 1. Identify leading run of // comments and blank lines, // which must be followed by a blank line. end := 0 @@ -819,6 +885,7 @@ func (ctxt *Context) shouldBuild(content []byte) bool { // Pass 2. Process each line in the run. p = content + allok := true for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { @@ -835,24 +902,24 @@ func (ctxt *Context) shouldBuild(content []byte) bool { if f[0] == "+build" { ok := false for _, tok := range f[1:] { - if ctxt.match(tok) { + if ctxt.match(tok, allTags) { ok = true - break } } if !ok { - return false // this one doesn't match + allok = false } } } } } - return true // everything matches + + return allok } // saveCgo saves the information from the #cgo lines in the import "C" comment. -// These lines set CFLAGS and LDFLAGS and pkg-config directives that affect -// the way cgo's C code is built. +// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives +// that affect the way cgo's C code is built. // // TODO(rsc): This duplicates code in cgo. // Once the dust settles, remove this code from cgo. @@ -887,7 +954,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) if len(cond) > 0 { ok := false for _, c := range cond { - if ctxt.match(c) { + if ctxt.match(c, nil) { ok = true break } @@ -902,7 +969,7 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig) } for _, arg := range args { - if !safeName(arg) { + if !safeCgoName(arg) { return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg) } } @@ -910,6 +977,10 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) switch verb { case "CFLAGS": di.CgoCFLAGS = append(di.CgoCFLAGS, args...) + case "CPPFLAGS": + di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...) + case "CXXFLAGS": + di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...) case "LDFLAGS": di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...) case "pkg-config": @@ -921,9 +992,12 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) return nil } -var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:") +// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN. +// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay. +// See golang.org/issue/6038. +var safeBytes = []byte("+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$") -func safeName(s string) bool { +func safeCgoName(s string) bool { if s == "" { return false } @@ -1008,19 +1082,28 @@ func splitQuoted(s string) (r []string, err error) { // !tag (if tag is not listed in ctxt.BuildTags or ctxt.ReleaseTags) // a comma-separated list of any of these // -func (ctxt *Context) match(name string) bool { +func (ctxt *Context) match(name string, allTags map[string]bool) bool { if name == "" { + if allTags != nil { + allTags[name] = true + } return false } if i := strings.Index(name, ","); i >= 0 { // comma-separated list - return ctxt.match(name[:i]) && ctxt.match(name[i+1:]) + ok1 := ctxt.match(name[:i], allTags) + ok2 := ctxt.match(name[i+1:], allTags) + return ok1 && ok2 } if strings.HasPrefix(name, "!!") { // bad syntax, reject always return false } if strings.HasPrefix(name, "!") { // negation - return len(name) > 1 && !ctxt.match(name[1:]) + return len(name) > 1 && !ctxt.match(name[1:], allTags) + } + + if allTags != nil { + allTags[name] = true } // Tags must be letters, digits, underscores or dots. @@ -1065,7 +1148,7 @@ func (ctxt *Context) match(name string) bool { // name_$(GOARCH)_test.* // name_$(GOOS)_$(GOARCH)_test.* // -func (ctxt *Context) goodOSArchFile(name string) bool { +func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { if dot := strings.Index(name, "."); dot != -1 { name = name[:dot] } @@ -1075,12 +1158,22 @@ func (ctxt *Context) goodOSArchFile(name string) bool { } n := len(l) if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] { + if allTags != nil { + allTags[l[n-2]] = true + allTags[l[n-1]] = true + } return l[n-2] == ctxt.GOOS && l[n-1] == ctxt.GOARCH } if n >= 1 && knownOS[l[n-1]] { + if allTags != nil { + allTags[l[n-1]] = true + } return l[n-1] == ctxt.GOOS } if n >= 1 && knownArch[l[n-1]] { + if allTags != nil { + allTags[l[n-1]] = true + } return l[n-1] == ctxt.GOARCH } return true diff --git a/libgo/go/go/build/build_test.go b/libgo/go/go/build/build_test.go index d8cf988..fca8d4b 100644 --- a/libgo/go/go/build/build_test.go +++ b/libgo/go/go/build/build_test.go @@ -5,38 +5,49 @@ package build import ( + "io" "os" "path/filepath" + "reflect" "runtime" + "strings" "testing" ) func TestMatch(t *testing.T) { ctxt := Default what := "default" - match := func(tag string) { - if !ctxt.match(tag) { + match := func(tag string, want map[string]bool) { + m := make(map[string]bool) + if !ctxt.match(tag, m) { t.Errorf("%s context should match %s, does not", what, tag) } + if !reflect.DeepEqual(m, want) { + t.Errorf("%s tags = %v, want %v", tag, m, want) + } } - nomatch := func(tag string) { - if ctxt.match(tag) { + nomatch := func(tag string, want map[string]bool) { + m := make(map[string]bool) + if ctxt.match(tag, m) { t.Errorf("%s context should NOT match %s, does", what, tag) } + if !reflect.DeepEqual(m, want) { + t.Errorf("%s tags = %v, want %v", tag, m, want) + } } - match(runtime.GOOS + "," + runtime.GOARCH) - match(runtime.GOOS + "," + runtime.GOARCH + ",!foo") - nomatch(runtime.GOOS + "," + runtime.GOARCH + ",foo") + match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true}) + match(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) + nomatch(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) what = "modified" ctxt.BuildTags = []string{"foo"} - match(runtime.GOOS + "," + runtime.GOARCH) - match(runtime.GOOS + "," + runtime.GOARCH + ",foo") - nomatch(runtime.GOOS + "," + runtime.GOARCH + ",!foo") - match(runtime.GOOS + "," + runtime.GOARCH + ",!bar") - nomatch(runtime.GOOS + "," + runtime.GOARCH + ",bar") - nomatch("!") + match(runtime.GOOS+","+runtime.GOARCH, map[string]bool{runtime.GOOS: true, runtime.GOARCH: true}) + match(runtime.GOOS+","+runtime.GOARCH+",foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) + nomatch(runtime.GOOS+","+runtime.GOARCH+",!foo", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "foo": true}) + match(runtime.GOOS+","+runtime.GOARCH+",!bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true}) + nomatch(runtime.GOOS+","+runtime.GOARCH+",bar", map[string]bool{runtime.GOOS: true, runtime.GOARCH: true, "bar": true}) + nomatch("!", map[string]bool{}) } func TestDotSlashImport(t *testing.T) { @@ -92,28 +103,84 @@ func TestLocalDirectory(t *testing.T) { func TestShouldBuild(t *testing.T) { const file1 = "// +build tag1\n\n" + "package main\n" + want1 := map[string]bool{"tag1": true} const file2 = "// +build cgo\n\n" + "// This package implements parsing of tags like\n" + "// +build tag1\n" + "package build" + want2 := map[string]bool{"cgo": true} const file3 = "// Copyright The Go Authors.\n\n" + "package build\n\n" + "// shouldBuild checks tags given by lines of the form\n" + "// +build tag\n" + "func shouldBuild(content []byte)\n" + want3 := map[string]bool{} ctx := &Context{BuildTags: []string{"tag1"}} - if !ctx.shouldBuild([]byte(file1)) { - t.Errorf("should not build file1, expected the contrary") + m := map[string]bool{} + if !ctx.shouldBuild([]byte(file1), m) { + t.Errorf("shouldBuild(file1) = false, want true") + } + if !reflect.DeepEqual(m, want1) { + t.Errorf("shoudBuild(file1) tags = %v, want %v", m, want1) + } + + m = map[string]bool{} + if ctx.shouldBuild([]byte(file2), m) { + t.Errorf("shouldBuild(file2) = true, want fakse") } - if ctx.shouldBuild([]byte(file2)) { - t.Errorf("should build file2, expected the contrary") + if !reflect.DeepEqual(m, want2) { + t.Errorf("shoudBuild(file2) tags = %v, want %v", m, want2) } + m = map[string]bool{} ctx = &Context{BuildTags: nil} - if !ctx.shouldBuild([]byte(file3)) { - t.Errorf("should not build file3, expected the contrary") + if !ctx.shouldBuild([]byte(file3), m) { + t.Errorf("shouldBuild(file3) = false, want true") + } + if !reflect.DeepEqual(m, want3) { + t.Errorf("shoudBuild(file3) tags = %v, want %v", m, want3) + } +} + +type readNopCloser struct { + io.Reader +} + +func (r readNopCloser) Close() error { + return nil +} + +var matchFileTests = []struct { + name string + data string + match bool +}{ + {"foo_arm.go", "", true}, + {"foo1_arm.go", "// +build linux\n\npackage main\n", false}, + {"foo_darwin.go", "", false}, + {"foo.go", "", true}, + {"foo1.go", "// +build linux\n\npackage main\n", false}, + {"foo.badsuffix", "", false}, +} + +func TestMatchFile(t *testing.T) { + for _, tt := range matchFileTests { + ctxt := Context{GOARCH: "arm", GOOS: "plan9"} + ctxt.OpenFile = func(path string) (r io.ReadCloser, err error) { + if path != "x+"+tt.name { + t.Fatalf("OpenFile asked for %q, expected %q", path, "x+"+tt.name) + } + return &readNopCloser{strings.NewReader(tt.data)}, nil + } + ctxt.JoinPath = func(elem ...string) string { + return strings.Join(elem, "+") + } + match, err := ctxt.MatchFile("x", tt.name) + if match != tt.match || err != nil { + t.Fatalf("MatchFile(%q) = %v, %v, want %v, nil", tt.name, match, err, tt.match) + } } } diff --git a/libgo/go/go/build/deps_test.go b/libgo/go/go/build/deps_test.go index 71b1bcf..dd162c7 100644 --- a/libgo/go/go/build/deps_test.go +++ b/libgo/go/go/build/deps_test.go @@ -82,24 +82,27 @@ var pkgDeps = map[string][]string{ // L3 adds reflection and some basic utility packages // and interface definitions, but nothing that makes // system calls. - "crypto": {"L2", "hash"}, // interfaces - "crypto/cipher": {"L2"}, // interfaces - "encoding/base32": {"L2"}, - "encoding/base64": {"L2"}, - "encoding/binary": {"L2", "reflect"}, - "hash": {"L2"}, // interfaces - "hash/adler32": {"L2", "hash"}, - "hash/crc32": {"L2", "hash"}, - "hash/crc64": {"L2", "hash"}, - "hash/fnv": {"L2", "hash"}, - "image": {"L2", "image/color"}, // interfaces - "image/color": {"L2"}, // interfaces - "reflect": {"L2"}, + "crypto": {"L2", "hash"}, // interfaces + "crypto/cipher": {"L2", "crypto/subtle"}, // interfaces + "crypto/subtle": {}, + "encoding/base32": {"L2"}, + "encoding/base64": {"L2"}, + "encoding/binary": {"L2", "reflect"}, + "hash": {"L2"}, // interfaces + "hash/adler32": {"L2", "hash"}, + "hash/crc32": {"L2", "hash"}, + "hash/crc64": {"L2", "hash"}, + "hash/fnv": {"L2", "hash"}, + "image": {"L2", "image/color"}, // interfaces + "image/color": {"L2"}, // interfaces + "image/color/palette": {"L2", "image/color"}, + "reflect": {"L2"}, "L3": { "L2", "crypto", "crypto/cipher", + "crypto/subtle", "encoding/base32", "encoding/base64", "encoding/binary", @@ -110,6 +113,7 @@ var pkgDeps = map[string][]string{ "hash/fnv", "image", "image/color", + "image/color/palette", "reflect", }, @@ -183,26 +187,27 @@ var pkgDeps = map[string][]string{ "compress/gzip": {"L4", "compress/flate"}, "compress/lzw": {"L4"}, "compress/zlib": {"L4", "compress/flate"}, - "database/sql": {"L4", "database/sql/driver"}, + "database/sql": {"L4", "container/list", "database/sql/driver"}, "database/sql/driver": {"L4", "time"}, "debug/dwarf": {"L4"}, "debug/elf": {"L4", "OS", "debug/dwarf"}, "debug/gosym": {"L4"}, "debug/macho": {"L4", "OS", "debug/dwarf"}, "debug/pe": {"L4", "OS", "debug/dwarf"}, + "encoding": {"L4"}, "encoding/ascii85": {"L4"}, "encoding/asn1": {"L4", "math/big"}, "encoding/csv": {"L4"}, - "encoding/gob": {"L4", "OS"}, + "encoding/gob": {"L4", "OS", "encoding"}, "encoding/hex": {"L4"}, - "encoding/json": {"L4"}, + "encoding/json": {"L4", "encoding"}, "encoding/pem": {"L4"}, - "encoding/xml": {"L4"}, + "encoding/xml": {"L4", "encoding"}, "flag": {"L4", "OS"}, "go/build": {"L4", "OS", "GOPARSER"}, "html": {"L4"}, "image/draw": {"L4"}, - "image/gif": {"L4", "compress/lzw"}, + "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"}, "image/jpeg": {"L4"}, "image/png": {"L4", "compress/zlib"}, "index/suffixarray": {"L4", "regexp"}, @@ -228,7 +233,8 @@ var pkgDeps = map[string][]string{ // that shows up in programs that use cgo. "C": {}, - "os/user": {"L4", "CGO", "syscall"}, + // Plan 9 alone needs io/ioutil and os. + "os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"}, // Basic networking. // Because net must be used by any package that wants to @@ -248,15 +254,10 @@ var pkgDeps = map[string][]string{ "net/mail": {"L4", "NET", "OS"}, "net/textproto": {"L4", "OS", "net"}, - // Support libraries for crypto that aren't L2. - "CRYPTO-SUPPORT": { - "crypto/subtle", - }, - // Core crypto. "crypto/aes": {"L3"}, "crypto/des": {"L3"}, - "crypto/hmac": {"L3", "CRYPTO-SUPPORT"}, + "crypto/hmac": {"L3"}, "crypto/md5": {"L3"}, "crypto/rc4": {"L3"}, "crypto/sha1": {"L3"}, @@ -264,7 +265,6 @@ var pkgDeps = map[string][]string{ "crypto/sha512": {"L3"}, "CRYPTO": { - "CRYPTO-SUPPORT", "crypto/aes", "crypto/des", "crypto/hmac", @@ -359,7 +359,7 @@ func allowed(pkg string) map[string]bool { } var bools = []bool{false, true} -var geese = []string{"darwin", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"} +var geese = []string{"darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "plan9", "windows"} var goarches = []string{"386", "amd64", "arm"} type osPkg struct { @@ -392,6 +392,9 @@ func TestDependencies(t *testing.T) { if allowedErrors[osPkg{ctxt.GOOS, pkg}] { continue } + if !ctxt.CgoEnabled && pkg == "runtime/cgo" { + continue + } // Some of the combinations we try might not // be reasonable (like arm,plan9,cgo), so ignore // errors for the auto-generated combinations. diff --git a/libgo/go/go/build/doc.go b/libgo/go/go/build/doc.go index b5fc071..b2f04ea 100644 --- a/libgo/go/go/build/doc.go +++ b/libgo/go/go/build/doc.go @@ -94,6 +94,7 @@ // - the compiler being used, either "gc" or "gccgo" // - "cgo", if ctxt.CgoEnabled is true // - "go1.1", from Go version 1.1 onward +// - "go1.2", from Go version 1.2 onward // - any additional words listed in ctxt.BuildTags // // If a file's name, after stripping the extension and a possible _test suffix, diff --git a/libgo/go/go/build/syslist_test.go b/libgo/go/go/build/syslist_test.go index 9157faf..3be2928 100644 --- a/libgo/go/go/build/syslist_test.go +++ b/libgo/go/go/build/syslist_test.go @@ -55,7 +55,7 @@ var tests = []GoodFileTest{ func TestGoodOSArch(t *testing.T) { for _, test := range tests { - if Default.goodOSArchFile(test.name) != test.result { + if Default.goodOSArchFile(test.name, make(map[string]bool)) != test.result { t.Fatalf("goodOSArchFile(%q) != %v", test.name, test.result) } } diff --git a/libgo/go/go/doc/comment.go b/libgo/go/go/doc/comment.go index c4b7e6a..5c8c43e 100644 --- a/libgo/go/go/doc/comment.go +++ b/libgo/go/go/doc/comment.go @@ -239,9 +239,14 @@ func anchorID(line string) string { // nor to have trailing spaces at the end of lines. // The comment markers have already been removed. // -// Turn each run of multiple \n into </p><p>. -// Turn each run of indented lines into a <pre> block without indent. -// Enclose headings with header tags. +// Each span of unindented non-blank lines is converted into +// a single paragraph. There is one exception to the rule: a span that +// consists of a single line, is followed by another paragraph span, +// begins with a capital letter, and contains no punctuation +// is formatted as a heading. +// +// A span of indented lines is converted into a <pre> block, +// with the common indent prefix removed. // // URLs in the comment text are converted into links; if the URL also appears // in the words map, the link is taken from the map (if the corresponding map diff --git a/libgo/go/go/doc/doc_test.go b/libgo/go/go/doc/doc_test.go index 8043038..ad8ba53 100644 --- a/libgo/go/go/doc/doc_test.go +++ b/libgo/go/go/doc/doc_test.go @@ -32,6 +32,7 @@ func readTemplate(filename string) *template.Template { t.Funcs(template.FuncMap{ "node": nodeFmt, "synopsis": synopsisFmt, + "indent": indentFmt, }) return template.Must(t.ParseFiles(filepath.Join(dataDir, filename))) } @@ -55,6 +56,15 @@ func synopsisFmt(s string) string { return "// " + strings.Replace(s, "\n", " ", -1) } +func indentFmt(indent, s string) string { + end := "" + if strings.HasSuffix(s, "\n") { + end = "\n" + s = s[:len(s)-1] + } + return indent + strings.Replace(s, "\n", "\n"+indent, -1) + end +} + func isGoFile(fi os.FileInfo) bool { name := fi.Name() return !fi.IsDir() && diff --git a/libgo/go/go/doc/example.go b/libgo/go/go/doc/example.go index 2761083..2358ed3 100644 --- a/libgo/go/go/doc/example.go +++ b/libgo/go/go/doc/example.go @@ -265,7 +265,7 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File { // Synthesize main function. funcDecl := &ast.FuncDecl{ Name: ast.NewIdent("main"), - Type: &ast.FuncType{}, + Type: &ast.FuncType{Params: &ast.FieldList{}}, // FuncType.Params must be non-nil Body: body, } diff --git a/libgo/go/go/doc/example_test.go b/libgo/go/go/doc/example_test.go index e0477e3..e154ea8 100644 --- a/libgo/go/go/doc/example_test.go +++ b/libgo/go/go/doc/example_test.go @@ -159,8 +159,8 @@ func main() { ` func TestExamples(t *testing.T) { - fs := token.NewFileSet() - file, err := parser.ParseFile(fs, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments) + fset := token.NewFileSet() + file, err := parser.ParseFile(fset, "test.go", strings.NewReader(exampleTestFile), parser.ParseComments) if err != nil { t.Fatal(err) } @@ -174,11 +174,11 @@ func TestExamples(t *testing.T) { if e.Play == nil { g = "<nil>" } else { - b := new(bytes.Buffer) - if err := format.Node(b, fs, e.Play); err != nil { + var buf bytes.Buffer + if err := format.Node(&buf, fset, e.Play); err != nil { t.Fatal(err) } - g = b.String() + g = buf.String() } if g != w { t.Errorf("%s: got Play == %q, want %q", c.Name, g, w) diff --git a/libgo/go/go/doc/reader.go b/libgo/go/go/doc/reader.go index 4fa6fd9..ed82c47 100644 --- a/libgo/go/go/doc/reader.go +++ b/libgo/go/go/doc/reader.go @@ -414,7 +414,7 @@ func (r *reader) readNote(list []*ast.Comment) { // We remove any formatting so that we don't // get spurious line breaks/indentation when // showing the TODO body. - body := clean(text[m[1]:]) + body := clean(text[m[1]:], keepNL) if body != "" { marker := text[m[2]:m[3]] r.notes[marker] = append(r.notes[marker], &Note{ diff --git a/libgo/go/go/doc/synopsis.go b/libgo/go/go/doc/synopsis.go index 2d18174..d1ad86c 100644 --- a/libgo/go/go/doc/synopsis.go +++ b/libgo/go/go/doc/synopsis.go @@ -27,14 +27,20 @@ func firstSentenceLen(s string) int { return len(s) } +const ( + keepNL = 1 << iota +) + // clean replaces each sequence of space, \n, \r, or \t characters // with a single space and removes any trailing and leading spaces. -func clean(s string) string { +// If the keepNL flag is set, newline characters are passed through +// instead of being change to spaces. +func clean(s string, flags int) string { var b []byte p := byte(' ') for i := 0; i < len(s); i++ { q := s[i] - if q == '\n' || q == '\r' || q == '\t' { + if (flags&keepNL) == 0 && q == '\n' || q == '\r' || q == '\t' { q = ' ' } if q != ' ' || p != ' ' { @@ -57,7 +63,7 @@ func clean(s string) string { // is the empty string. // func Synopsis(s string) string { - s = clean(s[0:firstSentenceLen(s)]) + s = clean(s[0:firstSentenceLen(s)], 0) for _, prefix := range IllegalPrefixes { if strings.HasPrefix(strings.ToLower(s), prefix) { return "" diff --git a/libgo/go/go/doc/testdata/a.0.golden b/libgo/go/go/doc/testdata/a.0.golden index cd98f4e..7e680b8 100644 --- a/libgo/go/go/doc/testdata/a.0.golden +++ b/libgo/go/go/doc/testdata/a.0.golden @@ -9,24 +9,44 @@ FILENAMES testdata/a1.go BUGS .Bugs is now deprecated, please use .Notes instead - // bug0 - // bug1 + bug0 + + bug1 + BUGS - // bug0 (uid: uid) - // bug1 (uid: uid) +BUG(uid) bug0 + +BUG(uid) bug1 + NOTES - // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo) - // 2 of 4 (uid: foo) - // 3 of 4 (uid: bar) - // 4 of 4 - this is the last line of note 4 (uid: bar) - // This note which contains a (parenthesized) subphrase must ... (uid: bam) - // The ':' after the marker and uid is optional. (uid: xxx) +NOTE(uid) + +NOTE(foo) 1 of 4 - this is the first line of note 1 + - note 1 continues on this 2nd line + - note 1 continues on this 3rd line + +NOTE(foo) 2 of 4 + +NOTE(bar) 3 of 4 + +NOTE(bar) 4 of 4 + - this is the last line of note 4 + +NOTE(bam) This note which contains a (parenthesized) subphrase + must appear in its entirety. + +NOTE(xxx) The ':' after the marker and uid is optional. + SECBUGS - // sec hole 0 need to fix asap (uid: uid) +SECBUG(uid) sec hole 0 + need to fix asap + TODOS - // todo0 (uid: uid) - // todo1 (uid: uid) +TODO(uid) todo0 + +TODO(uid) todo1 + diff --git a/libgo/go/go/doc/testdata/a.1.golden b/libgo/go/go/doc/testdata/a.1.golden index cd98f4e..7e680b8 100644 --- a/libgo/go/go/doc/testdata/a.1.golden +++ b/libgo/go/go/doc/testdata/a.1.golden @@ -9,24 +9,44 @@ FILENAMES testdata/a1.go BUGS .Bugs is now deprecated, please use .Notes instead - // bug0 - // bug1 + bug0 + + bug1 + BUGS - // bug0 (uid: uid) - // bug1 (uid: uid) +BUG(uid) bug0 + +BUG(uid) bug1 + NOTES - // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo) - // 2 of 4 (uid: foo) - // 3 of 4 (uid: bar) - // 4 of 4 - this is the last line of note 4 (uid: bar) - // This note which contains a (parenthesized) subphrase must ... (uid: bam) - // The ':' after the marker and uid is optional. (uid: xxx) +NOTE(uid) + +NOTE(foo) 1 of 4 - this is the first line of note 1 + - note 1 continues on this 2nd line + - note 1 continues on this 3rd line + +NOTE(foo) 2 of 4 + +NOTE(bar) 3 of 4 + +NOTE(bar) 4 of 4 + - this is the last line of note 4 + +NOTE(bam) This note which contains a (parenthesized) subphrase + must appear in its entirety. + +NOTE(xxx) The ':' after the marker and uid is optional. + SECBUGS - // sec hole 0 need to fix asap (uid: uid) +SECBUG(uid) sec hole 0 + need to fix asap + TODOS - // todo0 (uid: uid) - // todo1 (uid: uid) +TODO(uid) todo0 + +TODO(uid) todo1 + diff --git a/libgo/go/go/doc/testdata/a.2.golden b/libgo/go/go/doc/testdata/a.2.golden index cd98f4e..7e680b8 100644 --- a/libgo/go/go/doc/testdata/a.2.golden +++ b/libgo/go/go/doc/testdata/a.2.golden @@ -9,24 +9,44 @@ FILENAMES testdata/a1.go BUGS .Bugs is now deprecated, please use .Notes instead - // bug0 - // bug1 + bug0 + + bug1 + BUGS - // bug0 (uid: uid) - // bug1 (uid: uid) +BUG(uid) bug0 + +BUG(uid) bug1 + NOTES - // 1 of 4 - this is the first line of note 1 - note 1 continues on ... (uid: foo) - // 2 of 4 (uid: foo) - // 3 of 4 (uid: bar) - // 4 of 4 - this is the last line of note 4 (uid: bar) - // This note which contains a (parenthesized) subphrase must ... (uid: bam) - // The ':' after the marker and uid is optional. (uid: xxx) +NOTE(uid) + +NOTE(foo) 1 of 4 - this is the first line of note 1 + - note 1 continues on this 2nd line + - note 1 continues on this 3rd line + +NOTE(foo) 2 of 4 + +NOTE(bar) 3 of 4 + +NOTE(bar) 4 of 4 + - this is the last line of note 4 + +NOTE(bam) This note which contains a (parenthesized) subphrase + must appear in its entirety. + +NOTE(xxx) The ':' after the marker and uid is optional. + SECBUGS - // sec hole 0 need to fix asap (uid: uid) +SECBUG(uid) sec hole 0 + need to fix asap + TODOS - // todo0 (uid: uid) - // todo1 (uid: uid) +TODO(uid) todo0 + +TODO(uid) todo1 + diff --git a/libgo/go/go/doc/testdata/bugpara.0.golden b/libgo/go/go/doc/testdata/bugpara.0.golden new file mode 100644 index 0000000..5804859 --- /dev/null +++ b/libgo/go/go/doc/testdata/bugpara.0.golden @@ -0,0 +1,20 @@ +// +PACKAGE bugpara + +IMPORTPATH + testdata/bugpara + +FILENAMES + testdata/bugpara.go + +BUGS .Bugs is now deprecated, please use .Notes instead + Sometimes bugs have multiple paragraphs. + + Like this one. + + +BUGS +BUG(rsc) Sometimes bugs have multiple paragraphs. + + Like this one. + diff --git a/libgo/go/go/doc/testdata/bugpara.1.golden b/libgo/go/go/doc/testdata/bugpara.1.golden new file mode 100644 index 0000000..5804859 --- /dev/null +++ b/libgo/go/go/doc/testdata/bugpara.1.golden @@ -0,0 +1,20 @@ +// +PACKAGE bugpara + +IMPORTPATH + testdata/bugpara + +FILENAMES + testdata/bugpara.go + +BUGS .Bugs is now deprecated, please use .Notes instead + Sometimes bugs have multiple paragraphs. + + Like this one. + + +BUGS +BUG(rsc) Sometimes bugs have multiple paragraphs. + + Like this one. + diff --git a/libgo/go/go/doc/testdata/bugpara.2.golden b/libgo/go/go/doc/testdata/bugpara.2.golden new file mode 100644 index 0000000..5804859 --- /dev/null +++ b/libgo/go/go/doc/testdata/bugpara.2.golden @@ -0,0 +1,20 @@ +// +PACKAGE bugpara + +IMPORTPATH + testdata/bugpara + +FILENAMES + testdata/bugpara.go + +BUGS .Bugs is now deprecated, please use .Notes instead + Sometimes bugs have multiple paragraphs. + + Like this one. + + +BUGS +BUG(rsc) Sometimes bugs have multiple paragraphs. + + Like this one. + diff --git a/libgo/go/go/doc/testdata/bugpara.go b/libgo/go/go/doc/testdata/bugpara.go new file mode 100644 index 0000000..f5345a7 --- /dev/null +++ b/libgo/go/go/doc/testdata/bugpara.go @@ -0,0 +1,5 @@ +package bugpara + +// BUG(rsc): Sometimes bugs have multiple paragraphs. +// +// Like this one. diff --git a/libgo/go/go/doc/testdata/template.txt b/libgo/go/go/doc/testdata/template.txt index 26482f7..1b07382 100644 --- a/libgo/go/go/doc/testdata/template.txt +++ b/libgo/go/go/doc/testdata/template.txt @@ -61,8 +61,8 @@ TYPES */}}{{with .Bugs}} BUGS .Bugs is now deprecated, please use .Notes instead -{{range .}} {{synopsis .}} +{{range .}}{{indent "\t" .}} {{end}}{{end}}{{with .Notes}}{{range $marker, $content := .}} {{$marker}}S -{{range $content}} {{synopsis .Body}} (uid: {{.UID}}) +{{range $content}}{{$marker}}({{.UID}}){{indent "\t" .Body}} {{end}}{{end}}{{end}}
\ No newline at end of file diff --git a/libgo/go/go/doc/testdata/testing.0.golden b/libgo/go/go/doc/testdata/testing.0.golden index 15a9039..f8348f1 100644 --- a/libgo/go/go/doc/testdata/testing.0.golden +++ b/libgo/go/go/doc/testdata/testing.0.golden @@ -57,7 +57,7 @@ TYPES // FailNow marks the function as having failed and stops its ... func (c *B) FailNow() - // Failed returns whether the function has failed. + // Failed reports whether the function has failed. func (c *B) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). @@ -136,7 +136,7 @@ TYPES // FailNow marks the function as having failed and stops its ... func (c *T) FailNow() - // Failed returns whether the function has failed. + // Failed reports whether the function has failed. func (c *T) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). diff --git a/libgo/go/go/doc/testdata/testing.1.golden b/libgo/go/go/doc/testdata/testing.1.golden index ffdb5c3..282bb10 100644 --- a/libgo/go/go/doc/testdata/testing.1.golden +++ b/libgo/go/go/doc/testdata/testing.1.golden @@ -130,7 +130,7 @@ TYPES // FailNow marks the function as having failed and stops its ... func (c *B) FailNow() - // Failed returns whether the function has failed. + // Failed reports whether the function has failed. func (c *B) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). @@ -232,7 +232,7 @@ TYPES // FailNow marks the function as having failed and stops its ... func (c *T) FailNow() - // Failed returns whether the function has failed. + // Failed reports whether the function has failed. func (c *T) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). @@ -278,7 +278,7 @@ TYPES // FailNow marks the function as having failed and stops its ... func (c *common) FailNow() - // Failed returns whether the function has failed. + // Failed reports whether the function has failed. func (c *common) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). diff --git a/libgo/go/go/doc/testdata/testing.2.golden b/libgo/go/go/doc/testdata/testing.2.golden index 15a9039..f8348f1 100644 --- a/libgo/go/go/doc/testdata/testing.2.golden +++ b/libgo/go/go/doc/testdata/testing.2.golden @@ -57,7 +57,7 @@ TYPES // FailNow marks the function as having failed and stops its ... func (c *B) FailNow() - // Failed returns whether the function has failed. + // Failed reports whether the function has failed. func (c *B) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). @@ -136,7 +136,7 @@ TYPES // FailNow marks the function as having failed and stops its ... func (c *T) FailNow() - // Failed returns whether the function has failed. + // Failed reports whether the function has failed. func (c *T) Failed() bool // Fatal is equivalent to Log() followed by FailNow(). diff --git a/libgo/go/go/doc/testdata/testing.go b/libgo/go/go/doc/testdata/testing.go index c2499ad..93ed494 100644 --- a/libgo/go/go/doc/testdata/testing.go +++ b/libgo/go/go/doc/testdata/testing.go @@ -130,7 +130,7 @@ type T struct { // Fail marks the function as having failed but continues execution. func (c *common) Fail() { c.failed = true } -// Failed returns whether the function has failed. +// Failed reports whether the function has failed. func (c *common) Failed() bool { return c.failed } // FailNow marks the function as having failed and stops its execution. diff --git a/libgo/go/go/format/format_test.go b/libgo/go/go/format/format_test.go index 7d7940b..93f0992 100644 --- a/libgo/go/go/format/format_test.go +++ b/libgo/go/go/format/format_test.go @@ -90,7 +90,6 @@ var tests = []string{ "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings // erroneous programs - "ERRORvar x", "ERROR1 + 2 +", "ERRORx := 0", } diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go index 149257c..0f83ca9 100644 --- a/libgo/go/go/parser/interface.go +++ b/libgo/go/go/parser/interface.go @@ -15,6 +15,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" ) // If src != nil, readSource converts src to a []byte if possible; @@ -115,11 +116,13 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode) return } -// ParseDir calls ParseFile for the files in the directory specified by path and -// returns a map of package name -> package AST with all the packages found. If -// filter != nil, only the files with os.FileInfo entries passing through the filter -// are considered. The mode bits are passed to ParseFile unchanged. Position -// information is recorded in the file set fset. +// ParseDir calls ParseFile for all files with names ending in ".go" in the +// directory specified by path and returns a map of package name -> package +// AST with all the packages found. +// +// If filter != nil, only the files with os.FileInfo entries passing through +// the filter (and ending in ".go") are considered. The mode bits are passed +// to ParseFile unchanged. Position information is recorded in fset. // // If the directory couldn't be read, a nil map and the respective error are // returned. If a parse error occurred, a non-nil but incomplete map and the @@ -139,7 +142,7 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m pkgs = make(map[string]*ast.Package) for _, d := range list { - if filter == nil || filter(d) { + if strings.HasSuffix(d.Name(), ".go") && (filter == nil || filter(d)) { filename := filepath.Join(path, d.Name()) if src, err := ParseFile(fset, filename, nil, mode); err == nil { name := src.Name.Name diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go index f4a690a..c452331 100644 --- a/libgo/go/go/parser/parser.go +++ b/libgo/go/go/parser/parser.go @@ -64,7 +64,7 @@ type parser struct { } func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) { - p.file = fset.AddFile(filename, fset.Base(), len(src)) + p.file = fset.AddFile(filename, -1, len(src)) var m scanner.Mode if mode&ParseComments != 0 { m = scanner.ScanComments @@ -1150,7 +1150,7 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr { defer un(trace(p, "TypeAssertion")) } - p.expect(token.LPAREN) + lparen := p.expect(token.LPAREN) var typ ast.Expr if p.tok == token.TYPE { // type switch: typ == nil @@ -1158,9 +1158,9 @@ func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr { } else { typ = p.parseType() } - p.expect(token.RPAREN) + rparen := p.expect(token.RPAREN) - return &ast.TypeAssertExpr{X: x, Type: typ} + return &ast.TypeAssertExpr{X: x, Type: typ, Lparen: lparen, Rparen: rparen} } func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr { @@ -1170,25 +1170,27 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr { lbrack := p.expect(token.LBRACK) p.exprLev++ - var low, high ast.Expr - isSlice := false + var index [3]ast.Expr // change the 3 to 2 to disable slice expressions w/ cap if p.tok != token.COLON { - low = p.parseRhs() + index[0] = p.parseRhs() } - if p.tok == token.COLON { - isSlice = true + ncolons := 0 + for p.tok == token.COLON && ncolons < len(index)-1 { p.next() - if p.tok != token.RBRACK { - high = p.parseRhs() + ncolons++ + if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF { + index[ncolons] = p.parseRhs() } } p.exprLev-- rbrack := p.expect(token.RBRACK) - if isSlice { - return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: low, High: high, Rbrack: rbrack} + if ncolons > 0 { + // slice expression + return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: ncolons == 2, Rbrack: rbrack} } - return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: low, Rbrack: rbrack} + + return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack} } func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { @@ -1406,7 +1408,7 @@ L: } switch p.tok { case token.IDENT: - x = p.parseSelector(p.checkExpr(x)) + x = p.parseSelector(p.checkExprOrType(x)) case token.LPAREN: x = p.parseTypeAssertion(p.checkExpr(x)) default: @@ -2145,12 +2147,13 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as ident = p.parseIdent() } - var path *ast.BasicLit + pos := p.pos + var path string if p.tok == token.STRING { - if !isValidImport(p.lit) { - p.error(p.pos, "invalid import path: "+p.lit) + path = p.lit + if !isValidImport(path) { + p.error(pos, "invalid import path: "+path) } - path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit} p.next() } else { p.expect(token.STRING) // use expect() error handling @@ -2161,7 +2164,7 @@ func (p *parser) parseImportSpec(doc *ast.CommentGroup, _ token.Token, _ int) as spec := &ast.ImportSpec{ Doc: doc, Name: ident, - Path: path, + Path: &ast.BasicLit{ValuePos: pos, Kind: token.STRING, Value: path}, Comment: p.lineComment, } p.imports = append(p.imports, spec) @@ -2177,8 +2180,9 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota idents := p.parseIdentList() typ := p.tryType() var values []ast.Expr - if p.tok == token.ASSIGN || keyword == token.CONST && (typ != nil || iota == 0) || keyword == token.VAR && typ == nil { - p.expect(token.ASSIGN) + // always permit optional initialization for more tolerant parsing + if p.tok == token.ASSIGN { + p.next() values = p.parseRhsList() } p.expectSemi() // call before accessing p.linecomment @@ -2381,7 +2385,7 @@ func (p *parser) parseFile() *ast.File { // Go spec: The package clause is not a declaration; // the package name does not appear in any scope. ident := p.parseIdent() - if ident.Name == "_" { + if ident.Name == "_" && p.mode&DeclarationErrors != 0 { p.error(p.pos, "invalid package name _") } p.expectSemi() diff --git a/libgo/go/go/parser/parser_test.go b/libgo/go/go/parser/parser_test.go index 48813d1..0a34b7e 100644 --- a/libgo/go/go/parser/parser_test.go +++ b/libgo/go/go/parser/parser_test.go @@ -34,13 +34,12 @@ func TestParse(t *testing.T) { func nameFilter(filename string) bool { switch filename { - case "parser.go": - case "interface.go": - case "parser_test.go": - default: - return false + case "parser.go", "interface.go", "parser_test.go": + return true + case "parser.go.orig": + return true // permit but should be ignored by ParseDir } - return true + return false } func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) } @@ -51,14 +50,17 @@ func TestParseDir(t *testing.T) { if err != nil { t.Fatalf("ParseDir(%s): %v", path, err) } - if len(pkgs) != 1 { - t.Errorf("incorrect number of packages: %d", len(pkgs)) + if n := len(pkgs); n != 1 { + t.Errorf("got %d packages; want 1", n) } pkg := pkgs["parser"] if pkg == nil { t.Errorf(`package "parser" not found`) return } + if n := len(pkg.Files); n != 3 { + t.Errorf("got %d package files; want 3", n) + } for filename := range pkg.Files { if !nameFilter(filename) { t.Errorf("unexpected package file: %s", filename) diff --git a/libgo/go/go/parser/short_test.go b/libgo/go/go/parser/short_test.go index 62277c0..0ef0c56 100644 --- a/libgo/go/go/parser/short_test.go +++ b/libgo/go/go/parser/short_test.go @@ -33,6 +33,8 @@ var valids = []string{ `package p; func f() { if ; true {} };`, `package p; func f() { switch ; {} };`, `package p; func f() { for _ = range "foo" + "bar" {} };`, + `package p; func f() { var s []int; g(s[:], s[i:], s[:j], s[i:j], s[i:j:k], s[:j:k]) };`, + `package p; var ( _ = (struct {*T}).m; _ = (interface {T}).m )`, } func TestValid(t *testing.T) { @@ -46,7 +48,6 @@ var invalids = []string{ `package p; func f() { if { /* ERROR "expected operand" */ } };`, `package p; func f() { if ; { /* ERROR "expected operand" */ } };`, `package p; func f() { if f(); { /* ERROR "expected operand" */ } };`, - `package p; const c; /* ERROR "expected '='" */`, `package p; func f() { if _ /* ERROR "expected condition" */ = range x; true {} };`, `package p; func f() { switch _ /* ERROR "expected condition" */ = range x; true {} };`, `package p; func f() { for _ = range x ; /* ERROR "expected '{'" */ ; {} };`, @@ -74,6 +75,9 @@ var invalids = []string{ `package p; func f() { if x := g(); x = /* ERROR "expected '=='" */ 0 {}};`, `package p; func f() { _ = x = /* ERROR "expected '=='" */ 0 {}};`, `package p; func f() { _ = 1 == func()int { var x bool; x = x = /* ERROR "expected '=='" */ true; return x }() };`, + `package p; func f() { var s []int; _ = s[] /* ERROR "expected operand" */ };`, + `package p; func f() { var s []int; _ = s[::: /* ERROR "expected ']'" */ ] };`, + `package p; func f() { var s []int; _ = s[i:j:k: /* ERROR "expected ']'" */ l] };`, } func TestInvalid(t *testing.T) { diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go index 7cd068e..583c6c3 100644 --- a/libgo/go/go/printer/nodes.go +++ b/libgo/go/go/printer/nodes.go @@ -754,13 +754,13 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { case *ast.TypeAssertExpr: p.expr1(x.X, token.HighestPrec, depth) - p.print(token.PERIOD, token.LPAREN) + p.print(token.PERIOD, x.Lparen, token.LPAREN) if x.Type != nil { p.expr(x.Type) } else { p.print(token.TYPE) } - p.print(token.RPAREN) + p.print(x.Rparen, token.RPAREN) case *ast.IndexExpr: // TODO(gri): should treat[] like parentheses and undo one level of depth @@ -773,17 +773,25 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { // TODO(gri): should treat[] like parentheses and undo one level of depth p.expr1(x.X, token.HighestPrec, 1) p.print(x.Lbrack, token.LBRACK) - if x.Low != nil { - p.expr0(x.Low, depth+1) - } - // blanks around ":" if both sides exist and either side is a binary expression - if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) { - p.print(blank, token.COLON, blank) - } else { - p.print(token.COLON) + indices := []ast.Expr{x.Low, x.High} + if x.Max != nil { + indices = append(indices, x.Max) } - if x.High != nil { - p.expr0(x.High, depth+1) + for i, y := range indices { + if i > 0 { + // blanks around ":" if both sides exist and either side is a binary expression + // TODO(gri) once we have committed a variant of a[i:j:k] we may want to fine- + // tune the formatting here + x := indices[i-1] + if depth <= 1 && x != nil && y != nil && (isBinary(x) || isBinary(y)) { + p.print(blank, token.COLON, blank) + } else { + p.print(token.COLON) + } + } + if y != nil { + p.expr0(y, depth+1) + } } p.print(x.Rbrack, token.RBRACK) diff --git a/libgo/go/go/printer/testdata/expressions.golden b/libgo/go/go/printer/testdata/expressions.golden index 4291c55..fbe8275 100644 --- a/libgo/go/go/printer/testdata/expressions.golden +++ b/libgo/go/go/printer/testdata/expressions.golden @@ -114,6 +114,23 @@ func _() { x < y || z > 42 } +// slice expressions with cap +func _() { + _ = x[a:b:c] + _ = x[a:b : c+d] + _ = x[a : b+d : c] + _ = x[a : b+d : c+d] + _ = x[a+d : b:c] + _ = x[a+d : b : c+d] + _ = x[a+d : b+d : c] + _ = x[a+d : b+d : c+d] + + _ = x[:b:c] + _ = x[:b : c+d] + _ = x[:b+d : c] + _ = x[:b+d : c+d] +} + func _() { _ = a + b _ = a + b + c diff --git a/libgo/go/go/printer/testdata/expressions.input b/libgo/go/go/printer/testdata/expressions.input index 1ec12a0..f4d20fa 100644 --- a/libgo/go/go/printer/testdata/expressions.input +++ b/libgo/go/go/printer/testdata/expressions.input @@ -116,6 +116,23 @@ func _() { } +// slice expressions with cap +func _() { + _ = x[a:b:c] + _ = x[a:b:c+d] + _ = x[a:b+d:c] + _ = x[a:b+d:c+d] + _ = x[a+d:b:c] + _ = x[a+d:b:c+d] + _ = x[a+d:b+d:c] + _ = x[a+d:b+d:c+d] + + _ = x[:b:c] + _ = x[:b:c+d] + _ = x[:b+d:c] + _ = x[:b+d:c+d] +} + func _() { _ = a+b _ = a+b+c diff --git a/libgo/go/go/printer/testdata/expressions.raw b/libgo/go/go/printer/testdata/expressions.raw index 062900e..97bc81d 100644 --- a/libgo/go/go/printer/testdata/expressions.raw +++ b/libgo/go/go/printer/testdata/expressions.raw @@ -114,6 +114,23 @@ func _() { x < y || z > 42 } +// slice expressions with cap +func _() { + _ = x[a:b:c] + _ = x[a:b : c+d] + _ = x[a : b+d : c] + _ = x[a : b+d : c+d] + _ = x[a+d : b:c] + _ = x[a+d : b : c+d] + _ = x[a+d : b+d : c] + _ = x[a+d : b+d : c+d] + + _ = x[:b:c] + _ = x[:b : c+d] + _ = x[:b+d : c] + _ = x[:b+d : c+d] +} + func _() { _ = a + b _ = a + b + c diff --git a/libgo/go/go/token/position.go b/libgo/go/go/token/position.go index f5d9995..e6f0ae6 100644 --- a/libgo/go/go/token/position.go +++ b/libgo/go/go/token/position.go @@ -97,7 +97,7 @@ type File struct { size int // file size as provided to AddFile // lines and infos are protected by set.mutex - lines []int + lines []int // lines contains the offset of the first character for each line (the first entry is always 0) infos []lineInfo } @@ -136,6 +136,29 @@ func (f *File) AddLine(offset int) { f.set.mutex.Unlock() } +// MergeLine merges a line with the following line. It is akin to replacing +// the newline character at the end of the line with a space (to not change the +// remaining offsets). To obtain the line number, consult e.g. Position.Line. +// MergeLine will panic if given an invalid line number. +// +func (f *File) MergeLine(line int) { + if line <= 0 { + panic("illegal line number (line numbering starts at 1)") + } + f.set.mutex.Lock() + defer f.set.mutex.Unlock() + if line >= len(f.lines) { + panic("illegal line number") + } + // To merge the line numbered <line> with the line numbered <line+1>, + // we need to remove the entry in lines corresponding to the line + // numbered <line+1>. The entry in lines corresponding to the line + // numbered <line+1> is located at index <line>, since indices in lines + // are 0-based and line numbers are 1-based. + copy(f.lines[line:], f.lines[line+1:]) + f.lines = f.lines[:len(f.lines)-1] +} + // SetLines sets the line offsets for a file and returns true if successful. // The line offsets are the offsets of the first character of each line; // for instance for the content "ab\nc\n" the line offsets are {0, 3}. @@ -314,7 +337,8 @@ func (s *FileSet) Base() int { // AddFile adds a new file with a given filename, base offset, and file size // to the file set s and returns the file. Multiple files may have the same // name. The base offset must not be smaller than the FileSet's Base(), and -// size must not be negative. +// size must not be negative. As a special case, if a negative base is provided, +// the current value of the FileSet's Base() is used instead. // // Adding the file will set the file set's Base() value to base + size + 1 // as the minimum base value for the next file. The following relationship @@ -329,6 +353,9 @@ func (s *FileSet) Base() int { func (s *FileSet) AddFile(filename string, base, size int) *File { s.mutex.Lock() defer s.mutex.Unlock() + if base < 0 { + base = s.base + } if base < s.base || size < 0 { panic("illegal base or size") } diff --git a/libgo/go/go/token/position_test.go b/libgo/go/go/token/position_test.go index 1d36c22..ef6cfd9 100644 --- a/libgo/go/go/token/position_test.go +++ b/libgo/go/go/token/position_test.go @@ -167,7 +167,13 @@ func TestLineInfo(t *testing.T) { func TestFiles(t *testing.T) { fset := NewFileSet() for i, test := range tests { - fset.AddFile(test.filename, fset.Base(), test.size) + base := fset.Base() + if i%2 == 1 { + // Setting a negative base is equivalent to + // fset.Base(), so test some of each. + base = -1 + } + fset.AddFile(test.filename, base, test.size) j := 0 fset.Iterate(func(f *File) bool { if f.Name() != tests[j].filename { diff --git a/libgo/go/hash/hash.go b/libgo/go/hash/hash.go index aa895cf..8d138d0 100644 --- a/libgo/go/hash/hash.go +++ b/libgo/go/hash/hash.go @@ -9,7 +9,7 @@ import "io" // Hash is the common interface implemented by all hash functions. type Hash interface { - // Write adds more data to the running hash. + // Write (via the embedded io.Writer interface) adds more data to the running hash. // It never returns an error. io.Writer @@ -17,7 +17,7 @@ type Hash interface { // It does not change the underlying hash state. Sum(b []byte) []byte - // Reset resets the hash to one with zero bytes written. + // Reset resets the Hash to its initial state. Reset() // Size returns the number of bytes Sum will return. diff --git a/libgo/go/html/escape.go b/libgo/go/html/escape.go index eff0384..dd5dfa7 100644 --- a/libgo/go/html/escape.go +++ b/libgo/go/html/escape.go @@ -187,16 +187,6 @@ func unescape(b []byte) []byte { return b } -// lower lower-cases the A-Z bytes in b in-place, so that "aBc" becomes "abc". -func lower(b []byte) []byte { - for i, c := range b { - if 'A' <= c && c <= 'Z' { - b[i] = c + 'a' - 'A' - } - } - return b -} - const escapedChars = `&'<>"` func escape(w writer, s string) error { diff --git a/libgo/go/html/escape_test.go b/libgo/go/html/escape_test.go new file mode 100644 index 0000000..b405d4b --- /dev/null +++ b/libgo/go/html/escape_test.go @@ -0,0 +1,97 @@ +// 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 html + +import "testing" + +type unescapeTest struct { + // A short description of the test case. + desc string + // The HTML text. + html string + // The unescaped text. + unescaped string +} + +var unescapeTests = []unescapeTest{ + // Handle no entities. + { + "copy", + "A\ttext\nstring", + "A\ttext\nstring", + }, + // Handle simple named entities. + { + "simple", + "& > <", + "& > <", + }, + // Handle hitting the end of the string. + { + "stringEnd", + "& &", + "& &", + }, + // Handle entities with two codepoints. + { + "multiCodepoint", + "text ⋛︀ blah", + "text \u22db\ufe00 blah", + }, + // Handle decimal numeric entities. + { + "decimalEntity", + "Delta = Δ ", + "Delta = Δ ", + }, + // Handle hexadecimal numeric entities. + { + "hexadecimalEntity", + "Lambda = λ = λ ", + "Lambda = λ = λ ", + }, + // Handle numeric early termination. + { + "numericEnds", + "&# &#x €43 © = ©f = ©", + "&# &#x €43 © = ©f = ©", + }, + // Handle numeric ISO-8859-1 entity replacements. + { + "numericReplacements", + "Footnote‡", + "Footnote‡", + }, +} + +func TestUnescape(t *testing.T) { + for _, tt := range unescapeTests { + unescaped := UnescapeString(tt.html) + if unescaped != tt.unescaped { + t.Errorf("TestUnescape %s: want %q, got %q", tt.desc, tt.unescaped, unescaped) + } + } +} + +func TestUnescapeEscape(t *testing.T) { + ss := []string{ + ``, + `abc def`, + `a & b`, + `a&b`, + `a & b`, + `"`, + `"`, + `"<&>"`, + `"<&>"`, + `3&5==1 && 0<1, "0<1", a+acute=á`, + `The special characters are: <, >, &, ' and "`, + } + for _, s := range ss { + if got := UnescapeString(EscapeString(s)); got != s { + t.Errorf("got %q want %q", got, s) + } + } +} diff --git a/libgo/go/html/template/clone_test.go b/libgo/go/html/template/clone_test.go index 2663cdd..e11bff2 100644 --- a/libgo/go/html/template/clone_test.go +++ b/libgo/go/html/template/clone_test.go @@ -6,6 +6,8 @@ package template import ( "bytes" + "errors" + "io/ioutil" "testing" "text/template/parse" ) @@ -146,3 +148,41 @@ func TestCloneCrash(t *testing.T) { Must(t1.New("t1").Parse(`{{define "foo"}}foo{{end}}`)) t1.Clone() } + +// Ensure that this guarantee from the docs is upheld: +// "Further calls to Parse in the copy will add templates +// to the copy but not to the original." +func TestCloneThenParse(t *testing.T) { + t0 := Must(New("t0").Parse(`{{define "a"}}{{template "embedded"}}{{end}}`)) + t1 := Must(t0.Clone()) + Must(t1.Parse(`{{define "embedded"}}t1{{end}}`)) + if len(t0.Templates())+1 != len(t1.Templates()) { + t.Error("adding a template to a clone added it to the original") + } + // double check that the embedded template isn't available in the original + err := t0.ExecuteTemplate(ioutil.Discard, "a", nil) + if err == nil { + t.Error("expected 'no such template' error") + } +} + +// https://code.google.com/p/go/issues/detail?id=5980 +func TestFuncMapWorksAfterClone(t *testing.T) { + funcs := FuncMap{"customFunc": func() (string, error) { + return "", errors.New("issue5980") + }} + + // get the expected error output (no clone) + uncloned := Must(New("").Funcs(funcs).Parse("{{customFunc}}")) + wantErr := uncloned.Execute(ioutil.Discard, nil) + + // toClone must be the same as uncloned. It has to be recreated from scratch, + // since cloning cannot occur after execution. + toClone := Must(New("").Funcs(funcs).Parse("{{customFunc}}")) + cloned := Must(toClone.Clone()) + gotErr := cloned.Execute(ioutil.Discard, nil) + + if wantErr.Error() != gotErr.Error() { + t.Errorf("clone error message mismatch want %q got %q", wantErr, gotErr) + } +} diff --git a/libgo/go/html/template/content.go b/libgo/go/html/template/content.go index 9d1f74f..41b1116 100644 --- a/libgo/go/html/template/content.go +++ b/libgo/go/html/template/content.go @@ -74,6 +74,9 @@ const ( // indirect returns the value, after dereferencing as many times // as necessary to reach the base type (or nil). func indirect(a interface{}) interface{} { + if a == nil { + return nil + } if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr { // Avoid creating a reflect.Value if it's not a pointer. return a @@ -94,6 +97,9 @@ var ( // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer // or error, func indirectToStringerOrError(a interface{}) interface{} { + if a == nil { + return nil + } v := reflect.ValueOf(a) for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() diff --git a/libgo/go/html/template/content_test.go b/libgo/go/html/template/content_test.go index 3c32e5e..5f3ffe2 100644 --- a/libgo/go/html/template/content_test.go +++ b/libgo/go/html/template/content_test.go @@ -123,29 +123,29 @@ func TestTypedContent(t *testing.T) { { `<script>alert({{.}})</script>`, []string{ - `"\u003cb\u003e \"foo%\" O'Reilly &bar;"`, + `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, `"a[href =~ \"//example.com\"]#foo"`, - `"Hello, \u003cb\u003eWorld\u003c/b\u003e &tc!"`, + `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, `" dir=\"ltr\""`, // Not escaped. `c && alert("Hello, World!");`, // Escape sequence not over-escaped. `"Hello, World & O'Reilly\x21"`, - `"greeting=H%69&addressee=(World)"`, + `"greeting=H%69\u0026addressee=(World)"`, }, }, { `<button onclick="alert({{.}})">`, []string{ - `"\u003cb\u003e \"foo%\" O'Reilly &bar;"`, + `"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`, `"a[href =~ \"//example.com\"]#foo"`, - `"Hello, \u003cb\u003eWorld\u003c/b\u003e &amp;tc!"`, + `"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`, `" dir=\"ltr\""`, // Not JS escaped but HTML escaped. `c && alert("Hello, World!");`, // Escape sequence not over-escaped. `"Hello, World & O'Reilly\x21"`, - `"greeting=H%69&addressee=(World)"`, + `"greeting=H%69\u0026addressee=(World)"`, }, }, { @@ -259,3 +259,22 @@ func TestStringer(t *testing.T) { t.Errorf("expected %q got %q", expect, b.String()) } } + +// https://code.google.com/p/go/issues/detail?id=5982 +func TestEscapingNilNonemptyInterfaces(t *testing.T) { + tmpl := Must(New("x").Parse("{{.E}}")) + + got := new(bytes.Buffer) + testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand + tmpl.Execute(got, testData) + + // Use this data instead of just hard-coding "<nil>" to avoid + // dependencies on the html escaper and the behavior of fmt w.r.t. nil. + want := new(bytes.Buffer) + data := struct{ E string }{E: fmt.Sprint(nil)} + tmpl.Execute(want, data) + + if !bytes.Equal(want.Bytes(), got.Bytes()) { + t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes())) + } +} diff --git a/libgo/go/html/template/context.go b/libgo/go/html/template/context.go index 7202221..eb47e2b 100644 --- a/libgo/go/html/template/context.go +++ b/libgo/go/html/template/context.go @@ -29,7 +29,7 @@ func (c context) String() string { return fmt.Sprintf("{%v %v %v %v %v %v %v}", c.state, c.delim, c.urlPart, c.jsCtx, c.attr, c.element, c.err) } -// eq returns whether two contexts are equal. +// eq reports whether two contexts are equal. func (c context) eq(d context) bool { return c.state == d.state && c.delim == d.delim && diff --git a/libgo/go/html/template/css.go b/libgo/go/html/template/css.go index 3bcd984..634f183 100644 --- a/libgo/go/html/template/css.go +++ b/libgo/go/html/template/css.go @@ -11,7 +11,7 @@ import ( "unicode/utf8" ) -// endsWithCSSKeyword returns whether b ends with an ident that +// endsWithCSSKeyword reports whether b ends with an ident that // case-insensitively matches the lower-case kw. func endsWithCSSKeyword(b []byte, kw string) bool { i := len(b) - len(kw) @@ -34,7 +34,7 @@ func endsWithCSSKeyword(b []byte, kw string) bool { return string(bytes.ToLower(b[i:])) == kw } -// isCSSNmchar returns whether rune is allowed anywhere in a CSS identifier. +// isCSSNmchar reports whether rune is allowed anywhere in a CSS identifier. func isCSSNmchar(r rune) bool { // Based on the CSS3 nmchar production but ignores multi-rune escape // sequences. @@ -99,7 +99,7 @@ func decodeCSS(s []byte) []byte { return b } -// isHex returns whether the given character is a hex digit. +// isHex reports whether the given character is a hex digit. func isHex(c byte) bool { return '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' } @@ -144,7 +144,7 @@ func skipCSSSpace(c []byte) []byte { return c } -// isCSSSpace returns whether b is a CSS space char as defined in wc. +// isCSSSpace reports whether b is a CSS space char as defined in wc. func isCSSSpace(b byte) bool { switch b { case '\t', '\n', '\f', '\r', ' ': diff --git a/libgo/go/html/template/escape.go b/libgo/go/html/template/escape.go index 4829bfc..9ae9749d 100644 --- a/libgo/go/html/template/escape.go +++ b/libgo/go/html/template/escape.go @@ -35,11 +35,13 @@ func escapeTemplates(tmpl *Template, names ...string) error { for _, name := range names { if t := tmpl.set[name]; t != nil { t.text.Tree = nil + t.Tree = nil } } return err } tmpl.escaped = true + tmpl.Tree = tmpl.text.Tree } e.commit() return nil @@ -301,7 +303,7 @@ func indexOfStr(s string, strs []string, eq func(a, b string) bool) int { return -1 } -// escFnsEq returns whether the two escaping functions are equivalent. +// escFnsEq reports whether the two escaping functions are equivalent. func escFnsEq(a, b string) bool { if e := equivEscapers[a]; e != "" { a = e diff --git a/libgo/go/html/template/escape_test.go b/libgo/go/html/template/escape_test.go index de3659b..58383a6 100644 --- a/libgo/go/html/template/escape_test.go +++ b/libgo/go/html/template/escape_test.go @@ -538,7 +538,7 @@ func TestEscape(t *testing.T) { { "typed HTML in script", `<button onclick="alert({{.W}})">`, - `<button onclick="alert("&iexcl;\u003cb class=\"foo\"\u003eHello\u003c/b\u003e, \u003ctextarea\u003eO'World\u003c/textarea\u003e!")">`, + `<button onclick="alert("\u0026iexcl;\u003cb class=\"foo\"\u003eHello\u003c/b\u003e, \u003ctextarea\u003eO'World\u003c/textarea\u003e!")">`, }, { "typed HTML in RCDATA", @@ -655,6 +655,11 @@ func TestEscape(t *testing.T) { for _, test := range tests { tmpl := New(test.name) tmpl = Must(tmpl.Parse(test.input)) + // Check for bug 6459: Tree field was not set in Parse. + if tmpl.Tree != tmpl.text.Tree { + t.Errorf("%s: tree not set properly", test.name) + continue + } b := new(bytes.Buffer) if err := tmpl.Execute(b, data); err != nil { t.Errorf("%s: template execution failed: %s", test.name, err) @@ -673,6 +678,10 @@ func TestEscape(t *testing.T) { t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g) continue } + if tmpl.Tree != tmpl.text.Tree { + t.Errorf("%s: tree mismatch", test.name) + continue + } } } diff --git a/libgo/go/html/template/js.go b/libgo/go/html/template/js.go index a974093..d594e0a 100644 --- a/libgo/go/html/template/js.go +++ b/libgo/go/html/template/js.go @@ -341,7 +341,7 @@ var jsRegexpReplacementTable = []string{ '}': `\}`, } -// isJSIdentPart returns whether the given rune is a JS identifier part. +// isJSIdentPart reports whether the given rune is a JS identifier part. // It does not handle all the non-Latin letters, joiners, and combining marks, // but it does handle every codepoint that can occur in a numeric literal or // a keyword. diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go index e183898..11cc34a 100644 --- a/libgo/go/html/template/template.go +++ b/libgo/go/html/template/template.go @@ -21,7 +21,9 @@ type Template struct { // We could embed the text/template field, but it's safer not to because // we need to keep our version of the name space and the underlying // template's in sync. - text *template.Template + text *template.Template + // The underlying template's parse tree, updated to be HTML-safe. + Tree *parse.Tree *nameSpace // common to all associated templates } @@ -126,8 +128,10 @@ func (t *Template) Parse(src string) (*Template, error) { if tmpl == nil { tmpl = t.new(name) } + // Restore our record of this text/template to its unescaped original state. tmpl.escaped = false tmpl.text = v + tmpl.Tree = v.Tree } return t, nil } @@ -149,6 +153,7 @@ func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error ret := &Template{ false, text, + text.Tree, t.nameSpace, } t.set[name] = ret @@ -176,6 +181,7 @@ func (t *Template) Clone() (*Template, error) { ret := &Template{ false, textClone, + textClone.Tree, &nameSpace{ set: make(map[string]*Template), }, @@ -186,15 +192,11 @@ func (t *Template) Clone() (*Template, error) { if src == nil || src.escaped { return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name()) } - if x.Tree != nil { - x.Tree = &parse.Tree{ - Name: x.Tree.Name, - Root: x.Tree.Root.CopyList(), - } - } + x.Tree = x.Tree.Copy() ret.set[name] = &Template{ false, x, + x.Tree, ret.nameSpace, } } @@ -206,6 +208,7 @@ func New(name string) *Template { tmpl := &Template{ false, template.New(name), + nil, &nameSpace{ set: make(map[string]*Template), }, @@ -228,6 +231,7 @@ func (t *Template) new(name string) *Template { tmpl := &Template{ false, t.text.New(name), + nil, t.nameSpace, } tmpl.set[name] = tmpl diff --git a/libgo/go/html/template/transition.go b/libgo/go/html/template/transition.go index 564eb20..7f30a7a 100644 --- a/libgo/go/html/template/transition.go +++ b/libgo/go/html/template/transition.go @@ -504,12 +504,12 @@ var elementNameMap = map[string]element{ "title": elementTitle, } -// asciiAlpha returns whether c is an ASCII letter. +// asciiAlpha reports whether c is an ASCII letter. func asciiAlpha(c byte) bool { return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' } -// asciiAlphaNum returns whether c is an ASCII letter or digit. +// asciiAlphaNum reports whether c is an ASCII letter or digit. func asciiAlphaNum(c byte) bool { return asciiAlpha(c) || '0' <= c && c <= '9' } diff --git a/libgo/go/image/color/color.go b/libgo/go/image/color/color.go index 29a7b8a..ff596a7 100644 --- a/libgo/go/image/color/color.go +++ b/libgo/go/image/color/color.go @@ -253,13 +253,6 @@ func gray16Model(c Color) Color { // Palette is a palette of colors. type Palette []Color -func diff(a, b uint32) uint32 { - if a > b { - return a - b - } - return b - a -} - // Convert returns the palette color closest to c in Euclidean R,G,B space. func (p Palette) Convert(c Color) Color { if len(p) == 0 { @@ -271,19 +264,20 @@ func (p Palette) Convert(c Color) Color { // Index returns the index of the palette color closest to c in Euclidean // R,G,B space. func (p Palette) Index(c Color) int { + // A batch version of this computation is in image/draw/draw.go. + cr, cg, cb, _ := c.RGBA() - // Shift by 1 bit to avoid potential uint32 overflow in sum-squared-difference. - cr >>= 1 - cg >>= 1 - cb >>= 1 ret, bestSSD := 0, uint32(1<<32-1) for i, v := range p { vr, vg, vb, _ := v.RGBA() - vr >>= 1 - vg >>= 1 - vb >>= 1 - dr, dg, db := diff(cr, vr), diff(cg, vg), diff(cb, vb) - ssd := (dr * dr) + (dg * dg) + (db * db) + // We shift by 1 bit to avoid potential uint32 overflow in + // sum-squared-difference. + delta := (int32(cr) - int32(vr)) >> 1 + ssd := uint32(delta * delta) + delta = (int32(cg) - int32(vg)) >> 1 + ssd += uint32(delta * delta) + delta = (int32(cb) - int32(vb)) >> 1 + ssd += uint32(delta * delta) if ssd < bestSSD { if ssd == 0 { return i diff --git a/libgo/go/image/color/palette/gen.go b/libgo/go/image/color/palette/gen.go new file mode 100644 index 0000000..f20c021 --- /dev/null +++ b/libgo/go/image/color/palette/gen.go @@ -0,0 +1,97 @@ +// 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 ignore + +package main + +// This program generates palette.go. Invoke it as +// go run gen.go | gofmt > palette.go + +import ( + "fmt" +) + +func main() { + fmt.Println("// generated by go run gen.go; DO NOT EDIT") + fmt.Println() + fmt.Println("// Package palette provides standard color palettes.") + fmt.Println("package palette") + fmt.Println() + fmt.Println(`import "image/color"`) + fmt.Println() + printPlan9() + printWebSafe() +} + +func printPlan9() { + c, lines := [3]int{}, [256]string{} + for r, i := 0, 0; r != 4; r++ { + for v := 0; v != 4; v, i = v+1, i+16 { + for g, j := 0, v-r; g != 4; g++ { + for b := 0; b != 4; b, j = b+1, j+1 { + den := r + if g > den { + den = g + } + if b > den { + den = b + } + if den == 0 { + c[0] = 0x11 * v + c[1] = 0x11 * v + c[2] = 0x11 * v + } else { + num := 17 * (4*den + v) + c[0] = r * num / den + c[1] = g * num / den + c[2] = b * num / den + } + lines[i+(j&0x0f)] = + fmt.Sprintf("\tcolor.RGBA{0x%02x, 0x%02x, 0x%02x, 0xff},", c[0], c[1], c[2]) + } + } + } + } + fmt.Println("// Plan9 is a 256-color palette that partitions the 24-bit RGB space") + fmt.Println("// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the") + fmt.Println("// WebSafe, the idea is to reduce the color resolution by dicing the") + fmt.Println("// color cube into fewer cells, and to use the extra space to increase the") + fmt.Println("// intensity resolution. This results in 16 gray shades (4 gray subcubes with") + fmt.Println("// 4 samples in each), 13 shades of each primary and secondary color (3") + fmt.Println("// subcubes with 4 samples plus black) and a reasonable selection of colors") + fmt.Println("// covering the rest of the color cube. The advantage is better representation") + fmt.Println("// of continuous tones.") + fmt.Println("//") + fmt.Println("// This palette was used in the Plan 9 Operating System, described at") + fmt.Println("// http://plan9.bell-labs.com/magic/man2html/6/color") + fmt.Println("var Plan9 = []color.Color{") + for _, line := range lines { + fmt.Println(line) + } + fmt.Println("}") + fmt.Println() +} + +func printWebSafe() { + lines := [6 * 6 * 6]string{} + for r := 0; r < 6; r++ { + for g := 0; g < 6; g++ { + for b := 0; b < 6; b++ { + lines[36*r+6*g+b] = + fmt.Sprintf("\tcolor.RGBA{0x%02x, 0x%02x, 0x%02x, 0xff},", 0x33*r, 0x33*g, 0x33*b) + } + } + } + fmt.Println("// WebSafe is a 216-color palette that was popularized by early versions") + fmt.Println("// of Netscape Navigator. It is also known as the Netscape Color Cube.") + fmt.Println("//") + fmt.Println("// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details.") + fmt.Println("var WebSafe = []color.Color{") + for _, line := range lines { + fmt.Println(line) + } + fmt.Println("}") + fmt.Println() +} diff --git a/libgo/go/image/color/palette/palette.go b/libgo/go/image/color/palette/palette.go new file mode 100644 index 0000000..3aba740 --- /dev/null +++ b/libgo/go/image/color/palette/palette.go @@ -0,0 +1,500 @@ +// generated by go run gen.go; DO NOT EDIT + +// Package palette provides standard color palettes. +package palette + +import "image/color" + +// Plan9 is a 256-color palette that partitions the 24-bit RGB space +// into 4×4×4 subdivision, with 4 shades in each subcube. Compared to the +// WebSafe, the idea is to reduce the color resolution by dicing the +// color cube into fewer cells, and to use the extra space to increase the +// intensity resolution. This results in 16 gray shades (4 gray subcubes with +// 4 samples in each), 13 shades of each primary and secondary color (3 +// subcubes with 4 samples plus black) and a reasonable selection of colors +// covering the rest of the color cube. The advantage is better representation +// of continuous tones. +// +// This palette was used in the Plan 9 Operating System, described at +// http://plan9.bell-labs.com/magic/man2html/6/color +var Plan9 = []color.Color{ + color.RGBA{0x00, 0x00, 0x00, 0xff}, + color.RGBA{0x00, 0x00, 0x44, 0xff}, + color.RGBA{0x00, 0x00, 0x88, 0xff}, + color.RGBA{0x00, 0x00, 0xcc, 0xff}, + color.RGBA{0x00, 0x44, 0x00, 0xff}, + color.RGBA{0x00, 0x44, 0x44, 0xff}, + color.RGBA{0x00, 0x44, 0x88, 0xff}, + color.RGBA{0x00, 0x44, 0xcc, 0xff}, + color.RGBA{0x00, 0x88, 0x00, 0xff}, + color.RGBA{0x00, 0x88, 0x44, 0xff}, + color.RGBA{0x00, 0x88, 0x88, 0xff}, + color.RGBA{0x00, 0x88, 0xcc, 0xff}, + color.RGBA{0x00, 0xcc, 0x00, 0xff}, + color.RGBA{0x00, 0xcc, 0x44, 0xff}, + color.RGBA{0x00, 0xcc, 0x88, 0xff}, + color.RGBA{0x00, 0xcc, 0xcc, 0xff}, + color.RGBA{0x00, 0xdd, 0xdd, 0xff}, + color.RGBA{0x11, 0x11, 0x11, 0xff}, + color.RGBA{0x00, 0x00, 0x55, 0xff}, + color.RGBA{0x00, 0x00, 0x99, 0xff}, + color.RGBA{0x00, 0x00, 0xdd, 0xff}, + color.RGBA{0x00, 0x55, 0x00, 0xff}, + color.RGBA{0x00, 0x55, 0x55, 0xff}, + color.RGBA{0x00, 0x4c, 0x99, 0xff}, + color.RGBA{0x00, 0x49, 0xdd, 0xff}, + color.RGBA{0x00, 0x99, 0x00, 0xff}, + color.RGBA{0x00, 0x99, 0x4c, 0xff}, + color.RGBA{0x00, 0x99, 0x99, 0xff}, + color.RGBA{0x00, 0x93, 0xdd, 0xff}, + color.RGBA{0x00, 0xdd, 0x00, 0xff}, + color.RGBA{0x00, 0xdd, 0x49, 0xff}, + color.RGBA{0x00, 0xdd, 0x93, 0xff}, + color.RGBA{0x00, 0xee, 0x9e, 0xff}, + color.RGBA{0x00, 0xee, 0xee, 0xff}, + color.RGBA{0x22, 0x22, 0x22, 0xff}, + color.RGBA{0x00, 0x00, 0x66, 0xff}, + color.RGBA{0x00, 0x00, 0xaa, 0xff}, + color.RGBA{0x00, 0x00, 0xee, 0xff}, + color.RGBA{0x00, 0x66, 0x00, 0xff}, + color.RGBA{0x00, 0x66, 0x66, 0xff}, + color.RGBA{0x00, 0x55, 0xaa, 0xff}, + color.RGBA{0x00, 0x4f, 0xee, 0xff}, + color.RGBA{0x00, 0xaa, 0x00, 0xff}, + color.RGBA{0x00, 0xaa, 0x55, 0xff}, + color.RGBA{0x00, 0xaa, 0xaa, 0xff}, + color.RGBA{0x00, 0x9e, 0xee, 0xff}, + color.RGBA{0x00, 0xee, 0x00, 0xff}, + color.RGBA{0x00, 0xee, 0x4f, 0xff}, + color.RGBA{0x00, 0xff, 0x55, 0xff}, + color.RGBA{0x00, 0xff, 0xaa, 0xff}, + color.RGBA{0x00, 0xff, 0xff, 0xff}, + color.RGBA{0x33, 0x33, 0x33, 0xff}, + color.RGBA{0x00, 0x00, 0x77, 0xff}, + color.RGBA{0x00, 0x00, 0xbb, 0xff}, + color.RGBA{0x00, 0x00, 0xff, 0xff}, + color.RGBA{0x00, 0x77, 0x00, 0xff}, + color.RGBA{0x00, 0x77, 0x77, 0xff}, + color.RGBA{0x00, 0x5d, 0xbb, 0xff}, + color.RGBA{0x00, 0x55, 0xff, 0xff}, + color.RGBA{0x00, 0xbb, 0x00, 0xff}, + color.RGBA{0x00, 0xbb, 0x5d, 0xff}, + color.RGBA{0x00, 0xbb, 0xbb, 0xff}, + color.RGBA{0x00, 0xaa, 0xff, 0xff}, + color.RGBA{0x00, 0xff, 0x00, 0xff}, + color.RGBA{0x44, 0x00, 0x44, 0xff}, + color.RGBA{0x44, 0x00, 0x88, 0xff}, + color.RGBA{0x44, 0x00, 0xcc, 0xff}, + color.RGBA{0x44, 0x44, 0x00, 0xff}, + color.RGBA{0x44, 0x44, 0x44, 0xff}, + color.RGBA{0x44, 0x44, 0x88, 0xff}, + color.RGBA{0x44, 0x44, 0xcc, 0xff}, + color.RGBA{0x44, 0x88, 0x00, 0xff}, + color.RGBA{0x44, 0x88, 0x44, 0xff}, + color.RGBA{0x44, 0x88, 0x88, 0xff}, + color.RGBA{0x44, 0x88, 0xcc, 0xff}, + color.RGBA{0x44, 0xcc, 0x00, 0xff}, + color.RGBA{0x44, 0xcc, 0x44, 0xff}, + color.RGBA{0x44, 0xcc, 0x88, 0xff}, + color.RGBA{0x44, 0xcc, 0xcc, 0xff}, + color.RGBA{0x44, 0x00, 0x00, 0xff}, + color.RGBA{0x55, 0x00, 0x00, 0xff}, + color.RGBA{0x55, 0x00, 0x55, 0xff}, + color.RGBA{0x4c, 0x00, 0x99, 0xff}, + color.RGBA{0x49, 0x00, 0xdd, 0xff}, + color.RGBA{0x55, 0x55, 0x00, 0xff}, + color.RGBA{0x55, 0x55, 0x55, 0xff}, + color.RGBA{0x4c, 0x4c, 0x99, 0xff}, + color.RGBA{0x49, 0x49, 0xdd, 0xff}, + color.RGBA{0x4c, 0x99, 0x00, 0xff}, + color.RGBA{0x4c, 0x99, 0x4c, 0xff}, + color.RGBA{0x4c, 0x99, 0x99, 0xff}, + color.RGBA{0x49, 0x93, 0xdd, 0xff}, + color.RGBA{0x49, 0xdd, 0x00, 0xff}, + color.RGBA{0x49, 0xdd, 0x49, 0xff}, + color.RGBA{0x49, 0xdd, 0x93, 0xff}, + color.RGBA{0x49, 0xdd, 0xdd, 0xff}, + color.RGBA{0x4f, 0xee, 0xee, 0xff}, + color.RGBA{0x66, 0x00, 0x00, 0xff}, + color.RGBA{0x66, 0x00, 0x66, 0xff}, + color.RGBA{0x55, 0x00, 0xaa, 0xff}, + color.RGBA{0x4f, 0x00, 0xee, 0xff}, + color.RGBA{0x66, 0x66, 0x00, 0xff}, + color.RGBA{0x66, 0x66, 0x66, 0xff}, + color.RGBA{0x55, 0x55, 0xaa, 0xff}, + color.RGBA{0x4f, 0x4f, 0xee, 0xff}, + color.RGBA{0x55, 0xaa, 0x00, 0xff}, + color.RGBA{0x55, 0xaa, 0x55, 0xff}, + color.RGBA{0x55, 0xaa, 0xaa, 0xff}, + color.RGBA{0x4f, 0x9e, 0xee, 0xff}, + color.RGBA{0x4f, 0xee, 0x00, 0xff}, + color.RGBA{0x4f, 0xee, 0x4f, 0xff}, + color.RGBA{0x4f, 0xee, 0x9e, 0xff}, + color.RGBA{0x55, 0xff, 0xaa, 0xff}, + color.RGBA{0x55, 0xff, 0xff, 0xff}, + color.RGBA{0x77, 0x00, 0x00, 0xff}, + color.RGBA{0x77, 0x00, 0x77, 0xff}, + color.RGBA{0x5d, 0x00, 0xbb, 0xff}, + color.RGBA{0x55, 0x00, 0xff, 0xff}, + color.RGBA{0x77, 0x77, 0x00, 0xff}, + color.RGBA{0x77, 0x77, 0x77, 0xff}, + color.RGBA{0x5d, 0x5d, 0xbb, 0xff}, + color.RGBA{0x55, 0x55, 0xff, 0xff}, + color.RGBA{0x5d, 0xbb, 0x00, 0xff}, + color.RGBA{0x5d, 0xbb, 0x5d, 0xff}, + color.RGBA{0x5d, 0xbb, 0xbb, 0xff}, + color.RGBA{0x55, 0xaa, 0xff, 0xff}, + color.RGBA{0x55, 0xff, 0x00, 0xff}, + color.RGBA{0x55, 0xff, 0x55, 0xff}, + color.RGBA{0x88, 0x00, 0x88, 0xff}, + color.RGBA{0x88, 0x00, 0xcc, 0xff}, + color.RGBA{0x88, 0x44, 0x00, 0xff}, + color.RGBA{0x88, 0x44, 0x44, 0xff}, + color.RGBA{0x88, 0x44, 0x88, 0xff}, + color.RGBA{0x88, 0x44, 0xcc, 0xff}, + color.RGBA{0x88, 0x88, 0x00, 0xff}, + color.RGBA{0x88, 0x88, 0x44, 0xff}, + color.RGBA{0x88, 0x88, 0x88, 0xff}, + color.RGBA{0x88, 0x88, 0xcc, 0xff}, + color.RGBA{0x88, 0xcc, 0x00, 0xff}, + color.RGBA{0x88, 0xcc, 0x44, 0xff}, + color.RGBA{0x88, 0xcc, 0x88, 0xff}, + color.RGBA{0x88, 0xcc, 0xcc, 0xff}, + color.RGBA{0x88, 0x00, 0x00, 0xff}, + color.RGBA{0x88, 0x00, 0x44, 0xff}, + color.RGBA{0x99, 0x00, 0x4c, 0xff}, + color.RGBA{0x99, 0x00, 0x99, 0xff}, + color.RGBA{0x93, 0x00, 0xdd, 0xff}, + color.RGBA{0x99, 0x4c, 0x00, 0xff}, + color.RGBA{0x99, 0x4c, 0x4c, 0xff}, + color.RGBA{0x99, 0x4c, 0x99, 0xff}, + color.RGBA{0x93, 0x49, 0xdd, 0xff}, + color.RGBA{0x99, 0x99, 0x00, 0xff}, + color.RGBA{0x99, 0x99, 0x4c, 0xff}, + color.RGBA{0x99, 0x99, 0x99, 0xff}, + color.RGBA{0x93, 0x93, 0xdd, 0xff}, + color.RGBA{0x93, 0xdd, 0x00, 0xff}, + color.RGBA{0x93, 0xdd, 0x49, 0xff}, + color.RGBA{0x93, 0xdd, 0x93, 0xff}, + color.RGBA{0x93, 0xdd, 0xdd, 0xff}, + color.RGBA{0x99, 0x00, 0x00, 0xff}, + color.RGBA{0xaa, 0x00, 0x00, 0xff}, + color.RGBA{0xaa, 0x00, 0x55, 0xff}, + color.RGBA{0xaa, 0x00, 0xaa, 0xff}, + color.RGBA{0x9e, 0x00, 0xee, 0xff}, + color.RGBA{0xaa, 0x55, 0x00, 0xff}, + color.RGBA{0xaa, 0x55, 0x55, 0xff}, + color.RGBA{0xaa, 0x55, 0xaa, 0xff}, + color.RGBA{0x9e, 0x4f, 0xee, 0xff}, + color.RGBA{0xaa, 0xaa, 0x00, 0xff}, + color.RGBA{0xaa, 0xaa, 0x55, 0xff}, + color.RGBA{0xaa, 0xaa, 0xaa, 0xff}, + color.RGBA{0x9e, 0x9e, 0xee, 0xff}, + color.RGBA{0x9e, 0xee, 0x00, 0xff}, + color.RGBA{0x9e, 0xee, 0x4f, 0xff}, + color.RGBA{0x9e, 0xee, 0x9e, 0xff}, + color.RGBA{0x9e, 0xee, 0xee, 0xff}, + color.RGBA{0xaa, 0xff, 0xff, 0xff}, + color.RGBA{0xbb, 0x00, 0x00, 0xff}, + color.RGBA{0xbb, 0x00, 0x5d, 0xff}, + color.RGBA{0xbb, 0x00, 0xbb, 0xff}, + color.RGBA{0xaa, 0x00, 0xff, 0xff}, + color.RGBA{0xbb, 0x5d, 0x00, 0xff}, + color.RGBA{0xbb, 0x5d, 0x5d, 0xff}, + color.RGBA{0xbb, 0x5d, 0xbb, 0xff}, + color.RGBA{0xaa, 0x55, 0xff, 0xff}, + color.RGBA{0xbb, 0xbb, 0x00, 0xff}, + color.RGBA{0xbb, 0xbb, 0x5d, 0xff}, + color.RGBA{0xbb, 0xbb, 0xbb, 0xff}, + color.RGBA{0xaa, 0xaa, 0xff, 0xff}, + color.RGBA{0xaa, 0xff, 0x00, 0xff}, + color.RGBA{0xaa, 0xff, 0x55, 0xff}, + color.RGBA{0xaa, 0xff, 0xaa, 0xff}, + color.RGBA{0xcc, 0x00, 0xcc, 0xff}, + color.RGBA{0xcc, 0x44, 0x00, 0xff}, + color.RGBA{0xcc, 0x44, 0x44, 0xff}, + color.RGBA{0xcc, 0x44, 0x88, 0xff}, + color.RGBA{0xcc, 0x44, 0xcc, 0xff}, + color.RGBA{0xcc, 0x88, 0x00, 0xff}, + color.RGBA{0xcc, 0x88, 0x44, 0xff}, + color.RGBA{0xcc, 0x88, 0x88, 0xff}, + color.RGBA{0xcc, 0x88, 0xcc, 0xff}, + color.RGBA{0xcc, 0xcc, 0x00, 0xff}, + color.RGBA{0xcc, 0xcc, 0x44, 0xff}, + color.RGBA{0xcc, 0xcc, 0x88, 0xff}, + color.RGBA{0xcc, 0xcc, 0xcc, 0xff}, + color.RGBA{0xcc, 0x00, 0x00, 0xff}, + color.RGBA{0xcc, 0x00, 0x44, 0xff}, + color.RGBA{0xcc, 0x00, 0x88, 0xff}, + color.RGBA{0xdd, 0x00, 0x93, 0xff}, + color.RGBA{0xdd, 0x00, 0xdd, 0xff}, + color.RGBA{0xdd, 0x49, 0x00, 0xff}, + color.RGBA{0xdd, 0x49, 0x49, 0xff}, + color.RGBA{0xdd, 0x49, 0x93, 0xff}, + color.RGBA{0xdd, 0x49, 0xdd, 0xff}, + color.RGBA{0xdd, 0x93, 0x00, 0xff}, + color.RGBA{0xdd, 0x93, 0x49, 0xff}, + color.RGBA{0xdd, 0x93, 0x93, 0xff}, + color.RGBA{0xdd, 0x93, 0xdd, 0xff}, + color.RGBA{0xdd, 0xdd, 0x00, 0xff}, + color.RGBA{0xdd, 0xdd, 0x49, 0xff}, + color.RGBA{0xdd, 0xdd, 0x93, 0xff}, + color.RGBA{0xdd, 0xdd, 0xdd, 0xff}, + color.RGBA{0xdd, 0x00, 0x00, 0xff}, + color.RGBA{0xdd, 0x00, 0x49, 0xff}, + color.RGBA{0xee, 0x00, 0x4f, 0xff}, + color.RGBA{0xee, 0x00, 0x9e, 0xff}, + color.RGBA{0xee, 0x00, 0xee, 0xff}, + color.RGBA{0xee, 0x4f, 0x00, 0xff}, + color.RGBA{0xee, 0x4f, 0x4f, 0xff}, + color.RGBA{0xee, 0x4f, 0x9e, 0xff}, + color.RGBA{0xee, 0x4f, 0xee, 0xff}, + color.RGBA{0xee, 0x9e, 0x00, 0xff}, + color.RGBA{0xee, 0x9e, 0x4f, 0xff}, + color.RGBA{0xee, 0x9e, 0x9e, 0xff}, + color.RGBA{0xee, 0x9e, 0xee, 0xff}, + color.RGBA{0xee, 0xee, 0x00, 0xff}, + color.RGBA{0xee, 0xee, 0x4f, 0xff}, + color.RGBA{0xee, 0xee, 0x9e, 0xff}, + color.RGBA{0xee, 0xee, 0xee, 0xff}, + color.RGBA{0xee, 0x00, 0x00, 0xff}, + color.RGBA{0xff, 0x00, 0x00, 0xff}, + color.RGBA{0xff, 0x00, 0x55, 0xff}, + color.RGBA{0xff, 0x00, 0xaa, 0xff}, + color.RGBA{0xff, 0x00, 0xff, 0xff}, + color.RGBA{0xff, 0x55, 0x00, 0xff}, + color.RGBA{0xff, 0x55, 0x55, 0xff}, + color.RGBA{0xff, 0x55, 0xaa, 0xff}, + color.RGBA{0xff, 0x55, 0xff, 0xff}, + color.RGBA{0xff, 0xaa, 0x00, 0xff}, + color.RGBA{0xff, 0xaa, 0x55, 0xff}, + color.RGBA{0xff, 0xaa, 0xaa, 0xff}, + color.RGBA{0xff, 0xaa, 0xff, 0xff}, + color.RGBA{0xff, 0xff, 0x00, 0xff}, + color.RGBA{0xff, 0xff, 0x55, 0xff}, + color.RGBA{0xff, 0xff, 0xaa, 0xff}, + color.RGBA{0xff, 0xff, 0xff, 0xff}, +} + +// WebSafe is a 216-color palette that was popularized by early versions +// of Netscape Navigator. It is also known as the Netscape Color Cube. +// +// See http://en.wikipedia.org/wiki/Web_colors#Web-safe_colors for details. +var WebSafe = []color.Color{ + color.RGBA{0x00, 0x00, 0x00, 0xff}, + color.RGBA{0x00, 0x00, 0x33, 0xff}, + color.RGBA{0x00, 0x00, 0x66, 0xff}, + color.RGBA{0x00, 0x00, 0x99, 0xff}, + color.RGBA{0x00, 0x00, 0xcc, 0xff}, + color.RGBA{0x00, 0x00, 0xff, 0xff}, + color.RGBA{0x00, 0x33, 0x00, 0xff}, + color.RGBA{0x00, 0x33, 0x33, 0xff}, + color.RGBA{0x00, 0x33, 0x66, 0xff}, + color.RGBA{0x00, 0x33, 0x99, 0xff}, + color.RGBA{0x00, 0x33, 0xcc, 0xff}, + color.RGBA{0x00, 0x33, 0xff, 0xff}, + color.RGBA{0x00, 0x66, 0x00, 0xff}, + color.RGBA{0x00, 0x66, 0x33, 0xff}, + color.RGBA{0x00, 0x66, 0x66, 0xff}, + color.RGBA{0x00, 0x66, 0x99, 0xff}, + color.RGBA{0x00, 0x66, 0xcc, 0xff}, + color.RGBA{0x00, 0x66, 0xff, 0xff}, + color.RGBA{0x00, 0x99, 0x00, 0xff}, + color.RGBA{0x00, 0x99, 0x33, 0xff}, + color.RGBA{0x00, 0x99, 0x66, 0xff}, + color.RGBA{0x00, 0x99, 0x99, 0xff}, + color.RGBA{0x00, 0x99, 0xcc, 0xff}, + color.RGBA{0x00, 0x99, 0xff, 0xff}, + color.RGBA{0x00, 0xcc, 0x00, 0xff}, + color.RGBA{0x00, 0xcc, 0x33, 0xff}, + color.RGBA{0x00, 0xcc, 0x66, 0xff}, + color.RGBA{0x00, 0xcc, 0x99, 0xff}, + color.RGBA{0x00, 0xcc, 0xcc, 0xff}, + color.RGBA{0x00, 0xcc, 0xff, 0xff}, + color.RGBA{0x00, 0xff, 0x00, 0xff}, + color.RGBA{0x00, 0xff, 0x33, 0xff}, + color.RGBA{0x00, 0xff, 0x66, 0xff}, + color.RGBA{0x00, 0xff, 0x99, 0xff}, + color.RGBA{0x00, 0xff, 0xcc, 0xff}, + color.RGBA{0x00, 0xff, 0xff, 0xff}, + color.RGBA{0x33, 0x00, 0x00, 0xff}, + color.RGBA{0x33, 0x00, 0x33, 0xff}, + color.RGBA{0x33, 0x00, 0x66, 0xff}, + color.RGBA{0x33, 0x00, 0x99, 0xff}, + color.RGBA{0x33, 0x00, 0xcc, 0xff}, + color.RGBA{0x33, 0x00, 0xff, 0xff}, + color.RGBA{0x33, 0x33, 0x00, 0xff}, + color.RGBA{0x33, 0x33, 0x33, 0xff}, + color.RGBA{0x33, 0x33, 0x66, 0xff}, + color.RGBA{0x33, 0x33, 0x99, 0xff}, + color.RGBA{0x33, 0x33, 0xcc, 0xff}, + color.RGBA{0x33, 0x33, 0xff, 0xff}, + color.RGBA{0x33, 0x66, 0x00, 0xff}, + color.RGBA{0x33, 0x66, 0x33, 0xff}, + color.RGBA{0x33, 0x66, 0x66, 0xff}, + color.RGBA{0x33, 0x66, 0x99, 0xff}, + color.RGBA{0x33, 0x66, 0xcc, 0xff}, + color.RGBA{0x33, 0x66, 0xff, 0xff}, + color.RGBA{0x33, 0x99, 0x00, 0xff}, + color.RGBA{0x33, 0x99, 0x33, 0xff}, + color.RGBA{0x33, 0x99, 0x66, 0xff}, + color.RGBA{0x33, 0x99, 0x99, 0xff}, + color.RGBA{0x33, 0x99, 0xcc, 0xff}, + color.RGBA{0x33, 0x99, 0xff, 0xff}, + color.RGBA{0x33, 0xcc, 0x00, 0xff}, + color.RGBA{0x33, 0xcc, 0x33, 0xff}, + color.RGBA{0x33, 0xcc, 0x66, 0xff}, + color.RGBA{0x33, 0xcc, 0x99, 0xff}, + color.RGBA{0x33, 0xcc, 0xcc, 0xff}, + color.RGBA{0x33, 0xcc, 0xff, 0xff}, + color.RGBA{0x33, 0xff, 0x00, 0xff}, + color.RGBA{0x33, 0xff, 0x33, 0xff}, + color.RGBA{0x33, 0xff, 0x66, 0xff}, + color.RGBA{0x33, 0xff, 0x99, 0xff}, + color.RGBA{0x33, 0xff, 0xcc, 0xff}, + color.RGBA{0x33, 0xff, 0xff, 0xff}, + color.RGBA{0x66, 0x00, 0x00, 0xff}, + color.RGBA{0x66, 0x00, 0x33, 0xff}, + color.RGBA{0x66, 0x00, 0x66, 0xff}, + color.RGBA{0x66, 0x00, 0x99, 0xff}, + color.RGBA{0x66, 0x00, 0xcc, 0xff}, + color.RGBA{0x66, 0x00, 0xff, 0xff}, + color.RGBA{0x66, 0x33, 0x00, 0xff}, + color.RGBA{0x66, 0x33, 0x33, 0xff}, + color.RGBA{0x66, 0x33, 0x66, 0xff}, + color.RGBA{0x66, 0x33, 0x99, 0xff}, + color.RGBA{0x66, 0x33, 0xcc, 0xff}, + color.RGBA{0x66, 0x33, 0xff, 0xff}, + color.RGBA{0x66, 0x66, 0x00, 0xff}, + color.RGBA{0x66, 0x66, 0x33, 0xff}, + color.RGBA{0x66, 0x66, 0x66, 0xff}, + color.RGBA{0x66, 0x66, 0x99, 0xff}, + color.RGBA{0x66, 0x66, 0xcc, 0xff}, + color.RGBA{0x66, 0x66, 0xff, 0xff}, + color.RGBA{0x66, 0x99, 0x00, 0xff}, + color.RGBA{0x66, 0x99, 0x33, 0xff}, + color.RGBA{0x66, 0x99, 0x66, 0xff}, + color.RGBA{0x66, 0x99, 0x99, 0xff}, + color.RGBA{0x66, 0x99, 0xcc, 0xff}, + color.RGBA{0x66, 0x99, 0xff, 0xff}, + color.RGBA{0x66, 0xcc, 0x00, 0xff}, + color.RGBA{0x66, 0xcc, 0x33, 0xff}, + color.RGBA{0x66, 0xcc, 0x66, 0xff}, + color.RGBA{0x66, 0xcc, 0x99, 0xff}, + color.RGBA{0x66, 0xcc, 0xcc, 0xff}, + color.RGBA{0x66, 0xcc, 0xff, 0xff}, + color.RGBA{0x66, 0xff, 0x00, 0xff}, + color.RGBA{0x66, 0xff, 0x33, 0xff}, + color.RGBA{0x66, 0xff, 0x66, 0xff}, + color.RGBA{0x66, 0xff, 0x99, 0xff}, + color.RGBA{0x66, 0xff, 0xcc, 0xff}, + color.RGBA{0x66, 0xff, 0xff, 0xff}, + color.RGBA{0x99, 0x00, 0x00, 0xff}, + color.RGBA{0x99, 0x00, 0x33, 0xff}, + color.RGBA{0x99, 0x00, 0x66, 0xff}, + color.RGBA{0x99, 0x00, 0x99, 0xff}, + color.RGBA{0x99, 0x00, 0xcc, 0xff}, + color.RGBA{0x99, 0x00, 0xff, 0xff}, + color.RGBA{0x99, 0x33, 0x00, 0xff}, + color.RGBA{0x99, 0x33, 0x33, 0xff}, + color.RGBA{0x99, 0x33, 0x66, 0xff}, + color.RGBA{0x99, 0x33, 0x99, 0xff}, + color.RGBA{0x99, 0x33, 0xcc, 0xff}, + color.RGBA{0x99, 0x33, 0xff, 0xff}, + color.RGBA{0x99, 0x66, 0x00, 0xff}, + color.RGBA{0x99, 0x66, 0x33, 0xff}, + color.RGBA{0x99, 0x66, 0x66, 0xff}, + color.RGBA{0x99, 0x66, 0x99, 0xff}, + color.RGBA{0x99, 0x66, 0xcc, 0xff}, + color.RGBA{0x99, 0x66, 0xff, 0xff}, + color.RGBA{0x99, 0x99, 0x00, 0xff}, + color.RGBA{0x99, 0x99, 0x33, 0xff}, + color.RGBA{0x99, 0x99, 0x66, 0xff}, + color.RGBA{0x99, 0x99, 0x99, 0xff}, + color.RGBA{0x99, 0x99, 0xcc, 0xff}, + color.RGBA{0x99, 0x99, 0xff, 0xff}, + color.RGBA{0x99, 0xcc, 0x00, 0xff}, + color.RGBA{0x99, 0xcc, 0x33, 0xff}, + color.RGBA{0x99, 0xcc, 0x66, 0xff}, + color.RGBA{0x99, 0xcc, 0x99, 0xff}, + color.RGBA{0x99, 0xcc, 0xcc, 0xff}, + color.RGBA{0x99, 0xcc, 0xff, 0xff}, + color.RGBA{0x99, 0xff, 0x00, 0xff}, + color.RGBA{0x99, 0xff, 0x33, 0xff}, + color.RGBA{0x99, 0xff, 0x66, 0xff}, + color.RGBA{0x99, 0xff, 0x99, 0xff}, + color.RGBA{0x99, 0xff, 0xcc, 0xff}, + color.RGBA{0x99, 0xff, 0xff, 0xff}, + color.RGBA{0xcc, 0x00, 0x00, 0xff}, + color.RGBA{0xcc, 0x00, 0x33, 0xff}, + color.RGBA{0xcc, 0x00, 0x66, 0xff}, + color.RGBA{0xcc, 0x00, 0x99, 0xff}, + color.RGBA{0xcc, 0x00, 0xcc, 0xff}, + color.RGBA{0xcc, 0x00, 0xff, 0xff}, + color.RGBA{0xcc, 0x33, 0x00, 0xff}, + color.RGBA{0xcc, 0x33, 0x33, 0xff}, + color.RGBA{0xcc, 0x33, 0x66, 0xff}, + color.RGBA{0xcc, 0x33, 0x99, 0xff}, + color.RGBA{0xcc, 0x33, 0xcc, 0xff}, + color.RGBA{0xcc, 0x33, 0xff, 0xff}, + color.RGBA{0xcc, 0x66, 0x00, 0xff}, + color.RGBA{0xcc, 0x66, 0x33, 0xff}, + color.RGBA{0xcc, 0x66, 0x66, 0xff}, + color.RGBA{0xcc, 0x66, 0x99, 0xff}, + color.RGBA{0xcc, 0x66, 0xcc, 0xff}, + color.RGBA{0xcc, 0x66, 0xff, 0xff}, + color.RGBA{0xcc, 0x99, 0x00, 0xff}, + color.RGBA{0xcc, 0x99, 0x33, 0xff}, + color.RGBA{0xcc, 0x99, 0x66, 0xff}, + color.RGBA{0xcc, 0x99, 0x99, 0xff}, + color.RGBA{0xcc, 0x99, 0xcc, 0xff}, + color.RGBA{0xcc, 0x99, 0xff, 0xff}, + color.RGBA{0xcc, 0xcc, 0x00, 0xff}, + color.RGBA{0xcc, 0xcc, 0x33, 0xff}, + color.RGBA{0xcc, 0xcc, 0x66, 0xff}, + color.RGBA{0xcc, 0xcc, 0x99, 0xff}, + color.RGBA{0xcc, 0xcc, 0xcc, 0xff}, + color.RGBA{0xcc, 0xcc, 0xff, 0xff}, + color.RGBA{0xcc, 0xff, 0x00, 0xff}, + color.RGBA{0xcc, 0xff, 0x33, 0xff}, + color.RGBA{0xcc, 0xff, 0x66, 0xff}, + color.RGBA{0xcc, 0xff, 0x99, 0xff}, + color.RGBA{0xcc, 0xff, 0xcc, 0xff}, + color.RGBA{0xcc, 0xff, 0xff, 0xff}, + color.RGBA{0xff, 0x00, 0x00, 0xff}, + color.RGBA{0xff, 0x00, 0x33, 0xff}, + color.RGBA{0xff, 0x00, 0x66, 0xff}, + color.RGBA{0xff, 0x00, 0x99, 0xff}, + color.RGBA{0xff, 0x00, 0xcc, 0xff}, + color.RGBA{0xff, 0x00, 0xff, 0xff}, + color.RGBA{0xff, 0x33, 0x00, 0xff}, + color.RGBA{0xff, 0x33, 0x33, 0xff}, + color.RGBA{0xff, 0x33, 0x66, 0xff}, + color.RGBA{0xff, 0x33, 0x99, 0xff}, + color.RGBA{0xff, 0x33, 0xcc, 0xff}, + color.RGBA{0xff, 0x33, 0xff, 0xff}, + color.RGBA{0xff, 0x66, 0x00, 0xff}, + color.RGBA{0xff, 0x66, 0x33, 0xff}, + color.RGBA{0xff, 0x66, 0x66, 0xff}, + color.RGBA{0xff, 0x66, 0x99, 0xff}, + color.RGBA{0xff, 0x66, 0xcc, 0xff}, + color.RGBA{0xff, 0x66, 0xff, 0xff}, + color.RGBA{0xff, 0x99, 0x00, 0xff}, + color.RGBA{0xff, 0x99, 0x33, 0xff}, + color.RGBA{0xff, 0x99, 0x66, 0xff}, + color.RGBA{0xff, 0x99, 0x99, 0xff}, + color.RGBA{0xff, 0x99, 0xcc, 0xff}, + color.RGBA{0xff, 0x99, 0xff, 0xff}, + color.RGBA{0xff, 0xcc, 0x00, 0xff}, + color.RGBA{0xff, 0xcc, 0x33, 0xff}, + color.RGBA{0xff, 0xcc, 0x66, 0xff}, + color.RGBA{0xff, 0xcc, 0x99, 0xff}, + color.RGBA{0xff, 0xcc, 0xcc, 0xff}, + color.RGBA{0xff, 0xcc, 0xff, 0xff}, + color.RGBA{0xff, 0xff, 0x00, 0xff}, + color.RGBA{0xff, 0xff, 0x33, 0xff}, + color.RGBA{0xff, 0xff, 0x66, 0xff}, + color.RGBA{0xff, 0xff, 0x99, 0xff}, + color.RGBA{0xff, 0xff, 0xcc, 0xff}, + color.RGBA{0xff, 0xff, 0xff, 0xff}, +} diff --git a/libgo/go/image/decode_example_test.go b/libgo/go/image/decode_example_test.go index aa5a841..21e90fe 100644 --- a/libgo/go/image/decode_example_test.go +++ b/libgo/go/image/decode_example_test.go @@ -6,10 +6,11 @@ package image_test import ( + "encoding/base64" "fmt" "image" "log" - "os" + "strings" // Package image/jpeg is not used explicitly in the code below, // but is imported for its initialization side-effect, which allows @@ -21,15 +22,15 @@ import ( ) func Example() { - // Open the file. - file, err := os.Open("testdata/video-001.jpeg") - if err != nil { - log.Fatal(err) - } - defer file.Close() - - // Decode the image. - m, _, err := image.Decode(file) + // Decode the JPEG data. If reading from file, create a reader with + // + // reader, err := os.Open("testdata/video-001.q50.420.jpeg") + // if err != nil { + // log.Fatal(err) + // } + // defer reader.Close() + reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(data)) + m, _, err := image.Decode(reader) if err != nil { log.Fatal(err) } @@ -60,20 +61,80 @@ func Example() { } // Output: // bin red green blue alpha - // 0x0000-0x0fff: 471 819 7596 0 - // 0x1000-0x1fff: 576 2892 726 0 - // 0x2000-0x2fff: 1038 2330 943 0 - // 0x3000-0x3fff: 883 2321 1014 0 - // 0x4000-0x4fff: 501 1295 525 0 - // 0x5000-0x5fff: 302 962 242 0 - // 0x6000-0x6fff: 219 358 150 0 - // 0x7000-0x7fff: 352 281 192 0 - // 0x8000-0x8fff: 3688 216 246 0 - // 0x9000-0x9fff: 2277 237 283 0 - // 0xa000-0xafff: 971 254 357 0 - // 0xb000-0xbfff: 317 306 429 0 - // 0xc000-0xcfff: 203 402 401 0 - // 0xd000-0xdfff: 256 394 241 0 - // 0xe000-0xefff: 378 343 173 0 - // 0xf000-0xffff: 3018 2040 1932 15450 + // 0x0000-0x0fff: 353 759 7228 0 + // 0x1000-0x1fff: 629 2944 1036 0 + // 0x2000-0x2fff: 1075 2319 984 0 + // 0x3000-0x3fff: 838 2291 988 0 + // 0x4000-0x4fff: 540 1302 542 0 + // 0x5000-0x5fff: 319 971 263 0 + // 0x6000-0x6fff: 316 377 178 0 + // 0x7000-0x7fff: 581 280 216 0 + // 0x8000-0x8fff: 3457 228 274 0 + // 0x9000-0x9fff: 2294 237 334 0 + // 0xa000-0xafff: 938 283 370 0 + // 0xb000-0xbfff: 322 338 401 0 + // 0xc000-0xcfff: 229 386 295 0 + // 0xd000-0xdfff: 263 416 281 0 + // 0xe000-0xefff: 538 433 312 0 + // 0xf000-0xffff: 2758 1886 1748 15450 } + +const data = ` +/9j/4AAQSkZJRgABAQIAHAAcAAD/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdA +SFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2Nj +Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wAARCABnAJYDASIAAhEBAxEB/8QA +HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh +MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVW +V1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG +x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQF +BgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV +YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE +hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq +8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDlwKMD0pwzSiuK57QzGDxS7D6in8Y5ximnAPUfSlcq4m3ilUYp +2OKXHvRcVxnTtS7c07HNFK4DQPakC4PNOA+tOx70XAjK/So5gBGP94fzqfvUVx/qxx/EP51UXqRP4WSE +cmgjilP3jSEZqS0IO/NGDnpUiocDg/McDjvV6HTPOdVWYgsM5KcfzzQ2JySM2jp6VYu7SWzmMUwG4cgj +kMPUVBjjtTGtRu0Zopw+lFFxhinrGzuqqMsxAA9yaXFSRv5cqSEcIwYj6GpuZ30O30fSLKzhUpbpNMv3 +5XGTn29BV28jt7pPLuIVljPBBFVreYx+VbqAjycgt3x14zRcNOxGyVFHQkIc/wA61exyKLbuzjdZ046d +ftEuTEw3Rk9SPT8P8Kpbea3tchbyVae4JkjbbGpGdwOM89Af6ViFTWUtGdcXoM2+woK1JtpNtTcoZt+l +Jt7ZqTbRtouFyPFRXI/c9D94fzqzioLsfuD/ALw/nVReqIn8LJCOTSY+tSMOTmkIpXLRu+F0t5pJxPHG +wjjUAuBjJJz1+laD6Pai+WaK9SBX6puzn6ZP+NV/Dkdtc6ZNbyAFwxLAHDYPv6VoQ21nPNEEiQGEFRtk +Gf0NaWTOeW7Of8QwGG4MRZnEbYXPJwRnOR0zWNXW+KrqBLUWi5EjbWCgcAA9c/gRXKYqZaGlK/LqMH0F +FLtHvRSNiYD2pSDTgpp6p0ywUHoTULXYxcktzrdCf7Xo8LP/AKyEmMNjJ46dfbFWJ5TDGNwB9lFUvDV9 +YrbfYGbyrjcWG88S57g+vtV26ZIvMlumKwwjLZ6V0WfU54yTvYwtbubea2WNWbzg4bYQeBgj8OtYeKhj +u4y2HQxqxOD1xzxmrWAQCCGB6EGsaikndmsJxeiYzBo280/Z7UbayuaXGY5oIp+2lx9KLjIsVDeD/Rj/ +ALy/zq1t96r3y4tT/vL/ADq4P3kRP4WSleTSFKkkKoCW4GaqNcMxIjXj1pxjKT0FKrGC1Nrw3vGrKkYz +5kTAr6455/HH510UdwPtRgWCbzF5+YYUf4Vwun39xpmoR3qASMmQUJwGU9Rnt/8AWrpbrxhb8/ZdOmaQ +gAGZwFH5ZJrpVKVlY5ZYhN6kXiu2eO/ikZlIljAAB5yM549OawSOOlPuLqe+umuLqTfM4OSOAo7ADsKh +hl/cRsTuJHPv7mlKi3sVTxNtGP20VJhThgSQaK52mnZnUqsWrpkyeUrr5pABOAPU1AGaXUCWJISHGPfP +P8qL7BiKnsMg46H3qrbzupbj5mPTPTpXVSglG551SpzSsXJ4/MBUgYIxyKpySyGBYJriV1D7kRpCVH4V +bSeNJ4xchni3DeqnBI+td7F4b0mKIRjT45VbktJlzk455+n6VtYzv2PNwFZWBHBGKVJDGVC54/nXQeMN +NttLNkba1jgWVWDmM8bhg4/nzXLSSbXVj6fyNKUdNRp21RtIRJGrjuM0u3FQ2DbodvcEkfQmrW2vLqLl +k0ejCXNFMj2/jQV9qkxSYNRcsZiq2oI32N2CkhWXJxwOe9XMcVt6hoPn6dFaW0wgRpNzvKDlz6+/0rai +ryv2Jm9LHJai+ZRGCBjnr71ErdAxAY9B611t1Y2cunbbaOQ3FvKZI3UqGlZMbiWwfcfhV231iwvLSM3U +lt5Uq52TuZG+hGMA12xXJGxxzjzybOQtNOvb5j9ktZJhnBIHyg+5PFX38JayqK/2eLJIBUTgkDA9q7ex +itrSHFpGsUbndhRgc+g7VNIyfZJAoJZUbb3I46CtFJMylBo8sdWhmYMuCnylc9wef5VUT7+1chc5NS7h +sUZO5RtIPUH3pkBDOxxxmqM9TQtn+WilhHfHaik43KTG3Z4IyPyrNVjGCsZ+dmwv6V3cXhSG8sYpJLud +JJIwxChdoJGcYx/Wkg8DafA4knvLiQr/ALqj+VQpKw3FtnFFfvbiSMgZJ6/jXp2n3d9cQRBTFsKD96EP +oOxPU/8A68VVtbbRtMVntbePKDLTSHJH/Aj/AEqHTvE66rq72VugMMcbSGTnL4wMAfjT5n0HyW3L+s6b +baxaJBdzN+7bcrxkAhun0rz3VNCv7e7lgigknWI43xLu6jjIHTjtXqfkpPGVYsBkghTikgsYIN/lhgXb +cxLkknp/ShczQ7xtY8vtEmhkj8yGRBuCnehUcnHcVtmwfJ/fQ8e7f/E12txZW91C0U6b42xlST2OR/Ko +Bo1gM/uW55/1jf41nOipu7LhV5FZHIGzI6zwj/vr/Ck+yr3uYf8Ax7/CutbQdMb71tn/ALaN/jSf8I/p +X/PoP++2/wAan6rAr6wzkWt0II+1Rc/7Lf4Vd1eeCSKBbdZDdShYoiZNoyfY10P/AAj2lf8APmP++2/x +oPh/SjKspsozIuNrZORjp3qo0FHYPb3OZt7ae3SzjuItsiRSAgnccl/UA+3Q1yNjKLR4ZZYY5VD7tkv3 +WwO/+e1evPp9nI257aJm6bioz1z1+tY+s6Hplnot9PbWMMcqwOFcLyOO1bJWMZSTOPHi+9w3mosrlyd2 +9lCj02g9P/1e9a3hzxAbl2ikZRcdQueHHt7j864Y8Z4I4oRzG6urFWU5BHBB7HNJxTFGbR6he6Vpmtgm +eLy5zwZI/lb8fX8azIvBUUTHdfSFP4QsYB/HNZ+k+KEnRY75hHOvAk6K/v7H9K6yyvlnQBmDZ6GsnzR0 +N0oy1RzOtaN/Y1tHNFO06u+zYy4I4Jzx9KKveJblXuordSGES5b6n/62PzorKVdp2LjQTVyWz8UWEWlq +jSgyxfJt6EgdDzWTdeLIZGO7zHI/hVajGmWWP+PWL8qwlAIURrhpMAHHJA71pRcZrToZzcoEuo6heakA +GHk245CZ6/X1qPTLq40q+W5t2QybSpDAkEEc55/zilk5k2r91eKhLDzWz2rpsczbbuemeD76fUNG865I +MiysmQMZAAwa3a5j4ftu0ByP+fh/5CulkLLG7INzhSVHqe1Fh3uOoqn9qQQxyhndmHIxwOmSR2xQ13KD +KoiBZOV9JBnt707MVy5RWdNdy7wRGf3bfMinnO1jg+vY03WXLaJO3mhQ20b0zwpYf0qlG7S7icrJs08U +VwumgC+YiQyeVtZH567hzj8aSL949oGhE/2v5pJCDkksQwBHC4/+vXQ8LZ2uYxxCavY7us/xCcaBfn0h +b+VP0bnSrb94ZMJgOecj1rl/GfidUE2k2gy5+SeQjgA/wj3rlas2jdao48qrjLAGkSKPk4Gc1WMj92I+ +lIJnU8OfxPWo5inBokmtQTmM4OOh71b0q6vbFmWCbaxHyqQGAP0PT8KhSTzVyo5ocSKA5VfTOTmqsmRd +pl99XjPzThzK3zOeOSeveirNmkgg/fIpYsTkYORxRXmzlTjJqx6EVUcU7mhkKCzdAK59QI9zYxtG1fYU +UVtgtmY4nZEa8Ak9aqFv3rfSiiu1nMeifDv/AJF+T/r4f+QrqqKKQwzQenNFFMCOKFIgNuThdoJ5OPSk +ubeK6t3gnXdG4wwziiii/UTKMOg6dbzJLFE4dSCP3rEdeOM8805tDsGMvySgSsS6rM6gk9eAcUUVftZt +3uyVGNthuq3Eei6DK8H7sRR7YuMgHtXkc8rzTNLM26RyWY+p70UVnLY0iEsUipG7rhZBlDkc1HgYoorM +0HwyBXGeRjmrcUhMg2ghezd//rUUVcTKW5s2jZtY/QDaOKKKK8ip8bPRj8KP/9k= +` diff --git a/libgo/go/image/draw/draw.go b/libgo/go/image/draw/draw.go index 56d30dd..661230e 100644 --- a/libgo/go/image/draw/draw.go +++ b/libgo/go/image/draw/draw.go @@ -16,6 +16,19 @@ import ( // m is the maximum color value returned by image.Color.RGBA. const m = 1<<16 - 1 +// Image is an image.Image with a Set method to change a single pixel. +type Image interface { + image.Image + Set(x, y int, c color.Color) +} + +// Quantizer produces a palette for an image. +type Quantizer interface { + // Quantize appends up to cap(p) - len(p) colors to p and returns the + // updated palette suitable for converting m to a paletted image. + Quantize(p color.Palette, m image.Image) color.Palette +} + // Op is a Porter-Duff compositing operator. type Op int @@ -26,15 +39,31 @@ const ( Src ) -// A draw.Image is an image.Image with a Set method to change a single pixel. -type Image interface { - image.Image - Set(x, y int, c color.Color) +// Draw implements the Drawer interface by calling the Draw function with this +// Op. +func (op Op) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { + DrawMask(dst, r, src, sp, nil, image.Point{}, op) } -// Draw calls DrawMask with a nil mask. -func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) { - DrawMask(dst, r, src, sp, nil, image.ZP, op) +// Drawer contains the Draw method. +type Drawer interface { + // Draw aligns r.Min in dst with sp in src and then replaces the + // rectangle r in dst with the result of drawing src on dst. + Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) +} + +// FloydSteinberg is a Drawer that is the Src Op with Floyd-Steinberg error +// diffusion. +var FloydSteinberg Drawer = floydSteinberg{} + +type floydSteinberg struct{} + +func (floydSteinberg) Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { + clip(dst, &r, src, &sp, nil, nil) + if r.Empty() { + return + } + drawPaletted(dst, r, src, sp, true) } // clip clips r against each image's bounds (after translating into the @@ -58,6 +87,17 @@ func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask (*mp).Y += dy } +func processBackward(dst Image, r image.Rectangle, src image.Image, sp image.Point) bool { + return image.Image(dst) == src && + r.Overlaps(r.Add(sp.Sub(r.Min))) && + (sp.Y < r.Min.Y || (sp.Y == r.Min.Y && sp.X < r.Min.X)) +} + +// Draw calls DrawMask with a nil mask. +func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op) { + DrawMask(dst, r, src, sp, nil, image.Point{}, op) +} + // DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r // in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { @@ -67,7 +107,8 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas } // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. - if dst0, ok := dst.(*image.RGBA); ok { + switch dst0 := dst.(type) { + case *image.RGBA: if op == Over { if mask == nil { switch src0 := src.(type) { @@ -113,19 +154,20 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas } drawRGBA(dst0, r, src, sp, mask, mp, op) return + case *image.Paletted: + if op == Src && mask == nil && !processBackward(dst, r, src, sp) { + drawPaletted(dst0, r, src, sp, false) + } } x0, x1, dx := r.Min.X, r.Max.X, 1 y0, y1, dy := r.Min.Y, r.Max.Y, 1 - if image.Image(dst) == src && r.Overlaps(r.Add(sp.Sub(r.Min))) { - // Rectangles overlap: process backward? - if sp.Y < r.Min.Y || sp.Y == r.Min.Y && sp.X < r.Min.X { - x0, x1, dx = x1-1, x0-1, -1 - y0, y1, dy = y1-1, y0-1, -1 - } + if processBackward(dst, r, src, sp) { + x0, x1, dx = x1-1, x0-1, -1 + y0, y1, dy = y1-1, y0-1, -1 } - var out *color.RGBA64 + var out color.RGBA64 sy := sp.Y + y0 - r.Min.Y my := mp.Y + y0 - r.Min.Y for y := y0; y != y1; y, sy, my = y+dy, sy+dy, my+dy { @@ -147,9 +189,6 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas dst.Set(x, y, src.At(sx, sy)) default: sr, sg, sb, sa := src.At(sx, sy).RGBA() - if out == nil { - out = new(color.RGBA64) - } if op == Over { dr, dg, db, da := dst.At(x, y).RGBA() a := m - (sa * ma / m) @@ -163,7 +202,11 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas out.B = uint16(sb * ma / m) out.A = uint16(sa * ma / m) } - dst.Set(x, y, out) + // The third argument is &out instead of out (and out is + // declared outside of the inner loop) to avoid the implicit + // conversion to color.Color here allocating memory in the + // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). + dst.Set(x, y, &out) } } } @@ -500,3 +543,131 @@ func drawRGBA(dst *image.RGBA, r image.Rectangle, src image.Image, sp image.Poin i0 += dy * dst.Stride } } + +// clamp clamps i to the interval [0, 0xffff]. +func clamp(i int32) int32 { + if i < 0 { + return 0 + } + if i > 0xffff { + return 0xffff + } + return i +} + +func drawPaletted(dst Image, r image.Rectangle, src image.Image, sp image.Point, floydSteinberg bool) { + // TODO(nigeltao): handle the case where the dst and src overlap. + // Does it even make sense to try and do Floyd-Steinberg whilst + // walking the image backward (right-to-left bottom-to-top)? + + // If dst is an *image.Paletted, we have a fast path for dst.Set and + // dst.At. The dst.Set equivalent is a batch version of the algorithm + // used by color.Palette's Index method in image/color/color.go, plus + // optional Floyd-Steinberg error diffusion. + palette, pix, stride := [][3]int32(nil), []byte(nil), 0 + if p, ok := dst.(*image.Paletted); ok { + palette = make([][3]int32, len(p.Palette)) + for i, col := range p.Palette { + r, g, b, _ := col.RGBA() + palette[i][0] = int32(r) + palette[i][1] = int32(g) + palette[i][2] = int32(b) + } + pix, stride = p.Pix[p.PixOffset(r.Min.X, r.Min.Y):], p.Stride + } + + // quantErrorCurr and quantErrorNext are the Floyd-Steinberg quantization + // errors that have been propagated to the pixels in the current and next + // rows. The +2 simplifies calculation near the edges. + var quantErrorCurr, quantErrorNext [][3]int32 + if floydSteinberg { + quantErrorCurr = make([][3]int32, r.Dx()+2) + quantErrorNext = make([][3]int32, r.Dx()+2) + } + + // Loop over each source pixel. + out := color.RGBA64{A: 0xffff} + for y := 0; y != r.Dy(); y++ { + for x := 0; x != r.Dx(); x++ { + // er, eg and eb are the pixel's R,G,B values plus the + // optional Floyd-Steinberg error. + sr, sg, sb, _ := src.At(sp.X+x, sp.Y+y).RGBA() + er, eg, eb := int32(sr), int32(sg), int32(sb) + if floydSteinberg { + er = clamp(er + quantErrorCurr[x+1][0]/16) + eg = clamp(eg + quantErrorCurr[x+1][1]/16) + eb = clamp(eb + quantErrorCurr[x+1][2]/16) + } + + if palette != nil { + // Find the closest palette color in Euclidean R,G,B space: the + // one that minimizes sum-squared-difference. We shift by 1 bit + // to avoid potential uint32 overflow in sum-squared-difference. + // TODO(nigeltao): consider smarter algorithms. + bestIndex, bestSSD := 0, uint32(1<<32-1) + for index, p := range palette { + delta := (er - p[0]) >> 1 + ssd := uint32(delta * delta) + delta = (eg - p[1]) >> 1 + ssd += uint32(delta * delta) + delta = (eb - p[2]) >> 1 + ssd += uint32(delta * delta) + if ssd < bestSSD { + bestIndex, bestSSD = index, ssd + if ssd == 0 { + break + } + } + } + pix[y*stride+x] = byte(bestIndex) + + if !floydSteinberg { + continue + } + er -= int32(palette[bestIndex][0]) + eg -= int32(palette[bestIndex][1]) + eb -= int32(palette[bestIndex][2]) + + } else { + out.R = uint16(er) + out.G = uint16(eg) + out.B = uint16(eb) + // The third argument is &out instead of out (and out is + // declared outside of the inner loop) to avoid the implicit + // conversion to color.Color here allocating memory in the + // inner loop if sizeof(color.RGBA64) > sizeof(uintptr). + dst.Set(r.Min.X+x, r.Min.Y+y, &out) + + if !floydSteinberg { + continue + } + sr, sg, sb, _ = dst.At(r.Min.X+x, r.Min.Y+y).RGBA() + er -= int32(sr) + eg -= int32(sg) + eb -= int32(sb) + } + + // Propagate the Floyd-Steinberg quantization error. + quantErrorNext[x+0][0] += er * 3 + quantErrorNext[x+0][1] += eg * 3 + quantErrorNext[x+0][2] += eb * 3 + quantErrorNext[x+1][0] += er * 5 + quantErrorNext[x+1][1] += eg * 5 + quantErrorNext[x+1][2] += eb * 5 + quantErrorNext[x+2][0] += er * 1 + quantErrorNext[x+2][1] += eg * 1 + quantErrorNext[x+2][2] += eb * 1 + quantErrorCurr[x+2][0] += er * 7 + quantErrorCurr[x+2][1] += eg * 7 + quantErrorCurr[x+2][2] += eb * 7 + } + + // Recycle the quantization error buffers. + if floydSteinberg { + quantErrorCurr, quantErrorNext = quantErrorNext, quantErrorCurr + for i := range quantErrorNext { + quantErrorNext[i] = [3]int32{} + } + } + } +} diff --git a/libgo/go/image/draw/draw_test.go b/libgo/go/image/draw/draw_test.go index 1db75b3..0dd7fbd 100644 --- a/libgo/go/image/draw/draw_test.go +++ b/libgo/go/image/draw/draw_test.go @@ -7,6 +7,8 @@ package draw import ( "image" "image/color" + "image/png" + "os" "testing" ) @@ -352,3 +354,76 @@ func TestFill(t *testing.T) { check("whole") } } + +// TestFloydSteinbergCheckerboard tests that the result of Floyd-Steinberg +// error diffusion of a uniform 50% gray source image with a black-and-white +// palette is a checkerboard pattern. +func TestFloydSteinbergCheckerboard(t *testing.T) { + b := image.Rect(0, 0, 640, 480) + // We can't represent 50% exactly, but 0x7fff / 0xffff is close enough. + src := &image.Uniform{color.Gray16{0x7fff}} + dst := image.NewPaletted(b, color.Palette{color.Black, color.White}) + FloydSteinberg.Draw(dst, b, src, image.Point{}) + nErr := 0 + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + got := dst.Pix[dst.PixOffset(x, y)] + want := uint8(x+y) % 2 + if got != want { + t.Errorf("at (%d, %d): got %d, want %d", x, y, got, want) + if nErr++; nErr == 10 { + t.Fatal("there may be more errors") + } + } + } + } +} + +// embeddedPaletted is an Image that behaves like an *image.Paletted but whose +// type is not *image.Paletted. +type embeddedPaletted struct { + *image.Paletted +} + +// TestPaletted tests that the drawPaletted function behaves the same +// regardless of whether dst is an *image.Paletted. +func TestPaletted(t *testing.T) { + f, err := os.Open("../testdata/video-001.png") + if err != nil { + t.Fatalf("open: %v", err) + } + defer f.Close() + src, err := png.Decode(f) + if err != nil { + t.Fatalf("decode: %v", err) + } + b := src.Bounds() + + cgaPalette := color.Palette{ + color.RGBA{0x00, 0x00, 0x00, 0xff}, + color.RGBA{0x55, 0xff, 0xff, 0xff}, + color.RGBA{0xff, 0x55, 0xff, 0xff}, + color.RGBA{0xff, 0xff, 0xff, 0xff}, + } + drawers := map[string]Drawer{ + "src": Src, + "floyd-steinberg": FloydSteinberg, + } + +loop: + for dName, d := range drawers { + dst0 := image.NewPaletted(b, cgaPalette) + dst1 := image.NewPaletted(b, cgaPalette) + d.Draw(dst0, b, src, image.Point{}) + d.Draw(embeddedPaletted{dst1}, b, src, image.Point{}) + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + if !eq(dst0.At(x, y), dst1.At(x, y)) { + t.Errorf("%s: at (%d, %d), %v versus %v", + dName, x, y, dst0.At(x, y), dst1.At(x, y)) + continue loop + } + } + } + } +} diff --git a/libgo/go/image/format.go b/libgo/go/image/format.go index 36635bc..3668de4 100644 --- a/libgo/go/image/format.go +++ b/libgo/go/image/format.go @@ -47,7 +47,7 @@ func asReader(r io.Reader) reader { return bufio.NewReader(r) } -// Match returns whether magic matches b. Magic may contain "?" wildcards. +// Match reports whether magic matches b. Magic may contain "?" wildcards. func match(magic string, b []byte) bool { if len(magic) != len(b) { return false @@ -73,7 +73,7 @@ func sniff(r reader) format { // Decode decodes an image that has been encoded in a registered format. // The string returned is the format name used during format registration. -// Format registration is typically done by the init method of the codec- +// Format registration is typically done by an init function in the codec- // specific package. func Decode(r io.Reader) (Image, string, error) { rr := asReader(r) @@ -88,7 +88,7 @@ func Decode(r io.Reader) (Image, string, error) { // DecodeConfig decodes the color model and dimensions of an image that has // been encoded in a registered format. The string returned is the format name // used during format registration. Format registration is typically done by -// the init method of the codec-specific package. +// an init function in the codec-specific package. func DecodeConfig(r io.Reader) (Config, string, error) { rr := asReader(r) f := sniff(rr) diff --git a/libgo/go/image/geom.go b/libgo/go/image/geom.go index e123483..6ebaf67 100644 --- a/libgo/go/image/geom.go +++ b/libgo/go/image/geom.go @@ -38,7 +38,7 @@ func (p Point) Div(k int) Point { return Point{p.X / k, p.Y / k} } -// In returns whether p is in r. +// In reports whether p is in r. func (p Point) In(r Rectangle) bool { return r.Min.X <= p.X && p.X < r.Max.X && r.Min.Y <= p.Y && p.Y < r.Max.Y @@ -60,7 +60,7 @@ func (p Point) Mod(r Rectangle) Point { return p.Add(r.Min) } -// Eq returns whether p and q are equal. +// Eq reports whether p and q are equal. func (p Point) Eq(q Point) bool { return p.X == q.X && p.Y == q.Y } @@ -179,24 +179,24 @@ func (r Rectangle) Union(s Rectangle) Rectangle { return r } -// Empty returns whether the rectangle contains no points. +// Empty reports whether the rectangle contains no points. func (r Rectangle) Empty() bool { return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y } -// Eq returns whether r and s are equal. +// Eq reports whether r and s are equal. func (r Rectangle) Eq(s Rectangle) bool { return r.Min.X == s.Min.X && r.Min.Y == s.Min.Y && r.Max.X == s.Max.X && r.Max.Y == s.Max.Y } -// Overlaps returns whether r and s have a non-empty intersection. +// Overlaps reports whether r and s have a non-empty intersection. func (r Rectangle) Overlaps(s Rectangle) bool { return r.Min.X < s.Max.X && s.Min.X < r.Max.X && r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y } -// In returns whether every point in r is in s. +// In reports whether every point in r is in s. func (r Rectangle) In(s Rectangle) bool { if r.Empty() { return true diff --git a/libgo/go/image/gif/reader.go b/libgo/go/image/gif/reader.go index 8e8531f..8b0298a 100644 --- a/libgo/go/image/gif/reader.go +++ b/libgo/go/image/gif/reader.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package gif implements a GIF image decoder. +// Package gif implements a GIF image decoder and encoder. // // The GIF specification is at http://www.w3.org/Graphics/GIF/spec-gif89a.txt. package gif @@ -20,6 +20,7 @@ import ( var ( errNotEnough = errors.New("gif: not enough image data") errTooMuch = errors.New("gif: too much image data") + errBadPixel = errors.New("gif: invalid pixel value") ) // If the io.Reader does not also have ReadByte, then decode will introduce its own buffering. @@ -189,6 +190,7 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { // A wonderfully Go-like piece of magic. br := &blockReader{r: d.r} lzwr := lzw.NewReader(br, lzw.LSB, int(litWidth)) + defer lzwr.Close() if _, err = io.ReadFull(lzwr, m.Pix); err != nil { if err != io.ErrUnexpectedEOF { return err @@ -210,6 +212,15 @@ func (d *decoder) decode(r io.Reader, configOnly bool) error { return errTooMuch } + // Check that the color indexes are inside the palette. + if len(m.Palette) < 256 { + for _, pixel := range m.Pix { + if int(pixel) >= len(m.Palette) { + return errBadPixel + } + } + } + // Undo the interlacing if necessary. if d.imageFields&ifInterlace != 0 { uninterlace(m) diff --git a/libgo/go/image/gif/reader_test.go b/libgo/go/image/gif/reader_test.go index dcc6c6d..0986713 100644 --- a/libgo/go/image/gif/reader_test.go +++ b/libgo/go/image/gif/reader_test.go @@ -9,16 +9,16 @@ import ( "testing" ) -func TestDecode(t *testing.T) { - // header and trailer are parts of a valid 2x1 GIF image. - const ( - header = "GIF89a" + - "\x02\x00\x01\x00" + // width=2, height=1 - "\x80\x00\x00" + // headerFields=(a color map of 2 pixels), backgroundIndex, aspect - "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette - trailer = "\x3b" - ) +// header, palette and trailer are parts of a valid 2x1 GIF image. +const ( + headerStr = "GIF89a" + + "\x02\x00\x01\x00" + // width=2, height=1 + "\x80\x00\x00" // headerFields=(a color map of 2 pixels), backgroundIndex, aspect + paletteStr = "\x10\x20\x30\x40\x50\x60" // the color map, also known as a palette + trailerStr = "\x3b" +) +func TestDecode(t *testing.T) { // lzwEncode returns an LZW encoding (with 2-bit literals) of n zeroes. lzwEncode := func(n int) []byte { b := &bytes.Buffer{} @@ -41,7 +41,8 @@ func TestDecode(t *testing.T) { } for _, tc := range testCases { b := &bytes.Buffer{} - b.WriteString(header) + b.WriteString(headerStr) + b.WriteString(paletteStr) // Write an image with bounds 2x1 but tc.nPix pixels. If tc.nPix != 2 // then this should result in an invalid GIF image. First, write a // magic 0x2c (image descriptor) byte, bounds=(0,0)-(2,1), a flags @@ -60,7 +61,7 @@ func TestDecode(t *testing.T) { b.WriteString("\x01\x02") // A 1-byte payload with an 0x02 byte. } b.WriteByte(0x00) // An empty block signifies the end of the image data. - b.WriteString(trailer) + b.WriteString(trailerStr) got, err := Decode(b) if err != tc.wantErr { @@ -114,7 +115,7 @@ func try(t *testing.T, b []byte, want string) { } func TestBounds(t *testing.T) { - // make a local copy of testGIF + // Make a local copy of testGIF. gif := make([]byte, len(testGIF)) copy(gif, testGIF) // Make the bounds too big, just by one. @@ -136,3 +137,61 @@ func TestBounds(t *testing.T) { } try(t, gif, want) } + +func TestNoPalette(t *testing.T) { + b := &bytes.Buffer{} + + // Manufacture a GIF with no palette, so any pixel at all + // will be invalid. + b.WriteString(headerStr[:len(headerStr)-3]) + b.WriteString("\x00\x00\x00") // No global palette. + + // Image descriptor: 2x1, no local palette. + b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") + + // Encode the pixels: neither is in range, because there is no palette. + pix := []byte{0, 128} + enc := &bytes.Buffer{} + w := lzw.NewWriter(enc, lzw.LSB, 2) + w.Write(pix) + w.Close() + b.WriteByte(byte(len(enc.Bytes()))) + b.Write(enc.Bytes()) + b.WriteByte(0x00) // An empty block signifies the end of the image data. + + b.WriteString(trailerStr) + + try(t, b.Bytes(), "gif: invalid pixel value") +} + +func TestPixelOutsidePaletteRange(t *testing.T) { + for _, pval := range []byte{0, 1, 2, 3, 255} { + b := &bytes.Buffer{} + + // Manufacture a GIF with a 2 color palette. + b.WriteString(headerStr) + b.WriteString(paletteStr) + + // Image descriptor: 2x1, no local palette. + b.WriteString("\x2c\x00\x00\x00\x00\x02\x00\x01\x00\x00\x02") + + // Encode the pixels; some pvals trigger the expected error. + pix := []byte{pval, pval} + enc := &bytes.Buffer{} + w := lzw.NewWriter(enc, lzw.LSB, 2) + w.Write(pix) + w.Close() + b.WriteByte(byte(len(enc.Bytes()))) + b.Write(enc.Bytes()) + b.WriteByte(0x00) // An empty block signifies the end of the image data. + + b.WriteString(trailerStr) + + // No error expected, unless the pixels are beyond the 2 color palette. + want := "" + if pval >= 2 { + want = "gif: invalid pixel value" + } + try(t, b.Bytes(), want) + } +} diff --git a/libgo/go/image/gif/writer.go b/libgo/go/image/gif/writer.go new file mode 100644 index 0000000..15cd40f --- /dev/null +++ b/libgo/go/image/gif/writer.go @@ -0,0 +1,323 @@ +// 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 gif + +import ( + "bufio" + "compress/lzw" + "errors" + "image" + "image/color" + "image/color/palette" + "image/draw" + "io" +) + +// Graphic control extension fields. +const ( + gcLabel = 0xF9 + gcBlockSize = 0x04 +) + +var log2Lookup = [8]int{2, 4, 8, 16, 32, 64, 128, 256} + +func log2(x int) int { + for i, v := range log2Lookup { + if x <= v { + return i + } + } + return -1 +} + +// Little-endian. +func writeUint16(b []uint8, u uint16) { + b[0] = uint8(u) + b[1] = uint8(u >> 8) +} + +// writer is a buffered writer. +type writer interface { + Flush() error + io.Writer + io.ByteWriter +} + +// encoder encodes an image to the GIF format. +type encoder struct { + // w is the writer to write to. err is the first error encountered during + // writing. All attempted writes after the first error become no-ops. + w writer + err error + // g is a reference to the data that is being encoded. + g *GIF + // buf is a scratch buffer. It must be at least 768 so we can write the color map. + buf [1024]byte +} + +// blockWriter writes the block structure of GIF image data, which +// comprises (n, (n bytes)) blocks, with 1 <= n <= 255. It is the +// writer given to the LZW encoder, which is thus immune to the +// blocking. +type blockWriter struct { + e *encoder +} + +func (b blockWriter) Write(data []byte) (int, error) { + if b.e.err != nil { + return 0, b.e.err + } + if len(data) == 0 { + return 0, nil + } + total := 0 + for total < len(data) { + n := copy(b.e.buf[1:256], data[total:]) + total += n + b.e.buf[0] = uint8(n) + + n, b.e.err = b.e.w.Write(b.e.buf[:n+1]) + if b.e.err != nil { + return 0, b.e.err + } + } + return total, b.e.err +} + +func (e *encoder) flush() { + if e.err != nil { + return + } + e.err = e.w.Flush() +} + +func (e *encoder) write(p []byte) { + if e.err != nil { + return + } + _, e.err = e.w.Write(p) +} + +func (e *encoder) writeByte(b byte) { + if e.err != nil { + return + } + e.err = e.w.WriteByte(b) +} + +func (e *encoder) writeHeader() { + if e.err != nil { + return + } + _, e.err = io.WriteString(e.w, "GIF89a") + if e.err != nil { + return + } + + pm := e.g.Image[0] + // Logical screen width and height. + writeUint16(e.buf[0:2], uint16(pm.Bounds().Dx())) + writeUint16(e.buf[2:4], uint16(pm.Bounds().Dy())) + e.write(e.buf[:4]) + + // All frames have a local color table, so a global color table + // is not needed. + e.buf[0] = 0x00 + e.buf[1] = 0x00 // Background Color Index. + e.buf[2] = 0x00 // Pixel Aspect Ratio. + e.write(e.buf[:3]) + + // Add animation info if necessary. + if len(e.g.Image) > 1 { + e.buf[0] = 0x21 // Extension Introducer. + e.buf[1] = 0xff // Application Label. + e.buf[2] = 0x0b // Block Size. + e.write(e.buf[:3]) + _, e.err = io.WriteString(e.w, "NETSCAPE2.0") // Application Identifier. + if e.err != nil { + return + } + e.buf[0] = 0x03 // Block Size. + e.buf[1] = 0x01 // Sub-block Index. + writeUint16(e.buf[2:4], uint16(e.g.LoopCount)) + e.buf[4] = 0x00 // Block Terminator. + e.write(e.buf[:5]) + } +} + +func (e *encoder) writeColorTable(p color.Palette, size int) { + if e.err != nil { + return + } + + for i := 0; i < log2Lookup[size]; i++ { + if i < len(p) { + r, g, b, _ := p[i].RGBA() + e.buf[3*i+0] = uint8(r >> 8) + e.buf[3*i+1] = uint8(g >> 8) + e.buf[3*i+2] = uint8(b >> 8) + } else { + // Pad with black. + e.buf[3*i+0] = 0x00 + e.buf[3*i+1] = 0x00 + e.buf[3*i+2] = 0x00 + } + } + e.write(e.buf[:3*log2Lookup[size]]) +} + +func (e *encoder) writeImageBlock(pm *image.Paletted, delay int) { + if e.err != nil { + return + } + + if len(pm.Palette) == 0 { + e.err = errors.New("gif: cannot encode image block with empty palette") + return + } + + b := pm.Bounds() + if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 || b.Min.X < 0 || b.Min.X >= 1<<16 || b.Min.Y < 0 || b.Min.Y >= 1<<16 { + e.err = errors.New("gif: image block is too large to encode") + return + } + + transparentIndex := -1 + for i, c := range pm.Palette { + if _, _, _, a := c.RGBA(); a == 0 { + transparentIndex = i + break + } + } + + if delay > 0 || transparentIndex != -1 { + e.buf[0] = sExtension // Extension Introducer. + e.buf[1] = gcLabel // Graphic Control Label. + e.buf[2] = gcBlockSize // Block Size. + if transparentIndex != -1 { + e.buf[3] = 0x01 + } else { + e.buf[3] = 0x00 + } + writeUint16(e.buf[4:6], uint16(delay)) // Delay Time (1/100ths of a second) + + // Transparent color index. + if transparentIndex != -1 { + e.buf[6] = uint8(transparentIndex) + } else { + e.buf[6] = 0x00 + } + e.buf[7] = 0x00 // Block Terminator. + e.write(e.buf[:8]) + } + e.buf[0] = sImageDescriptor + writeUint16(e.buf[1:3], uint16(b.Min.X)) + writeUint16(e.buf[3:5], uint16(b.Min.Y)) + writeUint16(e.buf[5:7], uint16(b.Dx())) + writeUint16(e.buf[7:9], uint16(b.Dy())) + e.write(e.buf[:9]) + + paddedSize := log2(len(pm.Palette)) // Size of Local Color Table: 2^(1+n). + // Interlacing is not supported. + e.writeByte(0x80 | uint8(paddedSize)) + + // Local Color Table. + e.writeColorTable(pm.Palette, paddedSize) + + litWidth := paddedSize + 1 + if litWidth < 2 { + litWidth = 2 + } + e.writeByte(uint8(litWidth)) // LZW Minimum Code Size. + + lzww := lzw.NewWriter(blockWriter{e: e}, lzw.LSB, litWidth) + _, e.err = lzww.Write(pm.Pix) + if e.err != nil { + lzww.Close() + return + } + lzww.Close() + e.writeByte(0x00) // Block Terminator. +} + +// Options are the encoding parameters. +type Options struct { + // NumColors is the maximum number of colors used in the image. + // It ranges from 1 to 256. + NumColors int + + // Quantizer is used to produce a palette with size NumColors. + // palette.Plan9 is used in place of a nil Quantizer. + Quantizer draw.Quantizer + + // Drawer is used to convert the source image to the desired palette. + // draw.FloydSteinberg is used in place of a nil Drawer. + Drawer draw.Drawer +} + +// EncodeAll writes the images in g to w in GIF format with the +// given loop count and delay between frames. +func EncodeAll(w io.Writer, g *GIF) error { + if len(g.Image) == 0 { + return errors.New("gif: must provide at least one image") + } + + if len(g.Image) != len(g.Delay) { + return errors.New("gif: mismatched image and delay lengths") + } + if g.LoopCount < 0 { + g.LoopCount = 0 + } + + e := encoder{g: g} + if ww, ok := w.(writer); ok { + e.w = ww + } else { + e.w = bufio.NewWriter(w) + } + + e.writeHeader() + for i, pm := range g.Image { + e.writeImageBlock(pm, g.Delay[i]) + } + e.writeByte(sTrailer) + e.flush() + return e.err +} + +// Encode writes the Image m to w in GIF format. +func Encode(w io.Writer, m image.Image, o *Options) error { + // Check for bounds and size restrictions. + b := m.Bounds() + if b.Dx() >= 1<<16 || b.Dy() >= 1<<16 { + return errors.New("gif: image is too large to encode") + } + + opts := Options{} + if o != nil { + opts = *o + } + if opts.NumColors < 1 || 256 < opts.NumColors { + opts.NumColors = 256 + } + if opts.Drawer == nil { + opts.Drawer = draw.FloydSteinberg + } + + pm, ok := m.(*image.Paletted) + if !ok || len(pm.Palette) > opts.NumColors { + // TODO: Pick a better sub-sample of the Plan 9 palette. + pm = image.NewPaletted(b, palette.Plan9[:opts.NumColors]) + if opts.Quantizer != nil { + pm.Palette = opts.Quantizer.Quantize(make(color.Palette, 0, opts.NumColors), m) + } + opts.Drawer.Draw(pm, b, m, image.ZP) + } + + return EncodeAll(w, &GIF{ + Image: []*image.Paletted{pm}, + Delay: []int{0}, + }) +} diff --git a/libgo/go/image/gif/writer_test.go b/libgo/go/image/gif/writer_test.go new file mode 100644 index 0000000..c1ada76 --- /dev/null +++ b/libgo/go/image/gif/writer_test.go @@ -0,0 +1,204 @@ +// 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 gif + +import ( + "bytes" + "image" + "image/color" + _ "image/png" + "io/ioutil" + "math/rand" + "os" + "testing" +) + +func readImg(filename string) (image.Image, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + m, _, err := image.Decode(f) + return m, err +} + +func readGIF(filename string) (*GIF, error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + return DecodeAll(f) +} + +func delta(u0, u1 uint32) int64 { + d := int64(u0) - int64(u1) + if d < 0 { + return -d + } + return d +} + +// averageDelta returns the average delta in RGB space. The two images must +// have the same bounds. +func averageDelta(m0, m1 image.Image) int64 { + b := m0.Bounds() + var sum, n int64 + for y := b.Min.Y; y < b.Max.Y; y++ { + for x := b.Min.X; x < b.Max.X; x++ { + c0 := m0.At(x, y) + c1 := m1.At(x, y) + r0, g0, b0, _ := c0.RGBA() + r1, g1, b1, _ := c1.RGBA() + sum += delta(r0, r1) + sum += delta(g0, g1) + sum += delta(b0, b1) + n += 3 + } + } + return sum / n +} + +var testCase = []struct { + filename string + tolerance int64 +}{ + {"../testdata/video-001.png", 1 << 12}, + {"../testdata/video-001.gif", 0}, + {"../testdata/video-001.interlaced.gif", 0}, +} + +func TestWriter(t *testing.T) { + for _, tc := range testCase { + m0, err := readImg(tc.filename) + if err != nil { + t.Error(tc.filename, err) + continue + } + var buf bytes.Buffer + err = Encode(&buf, m0, nil) + if err != nil { + t.Error(tc.filename, err) + continue + } + m1, err := Decode(&buf) + if err != nil { + t.Error(tc.filename, err) + continue + } + if m0.Bounds() != m1.Bounds() { + t.Errorf("%s, bounds differ: %v and %v", tc.filename, m0.Bounds(), m1.Bounds()) + continue + } + // Compare the average delta to the tolerance level. + avgDelta := averageDelta(m0, m1) + if avgDelta > tc.tolerance { + t.Errorf("%s: average delta is too high. expected: %d, got %d", tc.filename, tc.tolerance, avgDelta) + continue + } + } +} + +var frames = []string{ + "../testdata/video-001.gif", + "../testdata/video-005.gray.gif", +} + +func TestEncodeAll(t *testing.T) { + g0 := &GIF{ + Image: make([]*image.Paletted, len(frames)), + Delay: make([]int, len(frames)), + LoopCount: 5, + } + for i, f := range frames { + m, err := readGIF(f) + if err != nil { + t.Error(f, err) + } + g0.Image[i] = m.Image[0] + } + var buf bytes.Buffer + if err := EncodeAll(&buf, g0); err != nil { + t.Fatal("EncodeAll:", err) + } + g1, err := DecodeAll(&buf) + if err != nil { + t.Fatal("DecodeAll:", err) + } + if g0.LoopCount != g1.LoopCount { + t.Errorf("loop counts differ: %d and %d", g0.LoopCount, g1.LoopCount) + } + for i := range g0.Image { + m0, m1 := g0.Image[i], g1.Image[i] + if m0.Bounds() != m1.Bounds() { + t.Errorf("%s, bounds differ: %v and %v", frames[i], m0.Bounds(), m1.Bounds()) + } + d0, d1 := g0.Delay[i], g1.Delay[i] + if d0 != d1 { + t.Errorf("%s: delay values differ: %d and %d", frames[i], d0, d1) + } + } + + g1.Delay = make([]int, 1) + if err := EncodeAll(ioutil.Discard, g1); err == nil { + t.Error("expected error from mismatched delay and image slice lengths") + } + if err := EncodeAll(ioutil.Discard, &GIF{}); err == nil { + t.Error("expected error from providing empty gif") + } +} + +func BenchmarkEncode(b *testing.B) { + b.StopTimer() + + bo := image.Rect(0, 0, 640, 480) + rnd := rand.New(rand.NewSource(123)) + + // Restrict to a 256-color paletted image to avoid quantization path. + palette := make(color.Palette, 256) + for i := range palette { + palette[i] = color.RGBA{ + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + 255, + } + } + img := image.NewPaletted(image.Rect(0, 0, 640, 480), palette) + for y := bo.Min.Y; y < bo.Max.Y; y++ { + for x := bo.Min.X; x < bo.Max.X; x++ { + img.Set(x, y, palette[rnd.Intn(256)]) + } + } + + b.SetBytes(640 * 480 * 4) + b.StartTimer() + for i := 0; i < b.N; i++ { + Encode(ioutil.Discard, img, nil) + } +} + +func BenchmarkQuantizedEncode(b *testing.B) { + b.StopTimer() + img := image.NewRGBA(image.Rect(0, 0, 640, 480)) + bo := img.Bounds() + rnd := rand.New(rand.NewSource(123)) + for y := bo.Min.Y; y < bo.Max.Y; y++ { + for x := bo.Min.X; x < bo.Max.X; x++ { + img.SetRGBA(x, y, color.RGBA{ + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + uint8(rnd.Intn(256)), + 255, + }) + } + } + b.SetBytes(640 * 480 * 4) + b.StartTimer() + for i := 0; i < b.N; i++ { + Encode(ioutil.Discard, img, nil) + } +} diff --git a/libgo/go/image/image.go b/libgo/go/image/image.go index 03ac606..32a89ef 100644 --- a/libgo/go/image/image.go +++ b/libgo/go/image/image.go @@ -126,7 +126,7 @@ func (p *RGBA) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *RGBA) Opaque() bool { if p.Rect.Empty() { return true @@ -234,7 +234,7 @@ func (p *RGBA64) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *RGBA64) Opaque() bool { if p.Rect.Empty() { return true @@ -329,7 +329,7 @@ func (p *NRGBA) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *NRGBA) Opaque() bool { if p.Rect.Empty() { return true @@ -437,7 +437,7 @@ func (p *NRGBA64) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *NRGBA64) Opaque() bool { if p.Rect.Empty() { return true @@ -525,7 +525,7 @@ func (p *Alpha) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *Alpha) Opaque() bool { if p.Rect.Empty() { return true @@ -616,7 +616,7 @@ func (p *Alpha16) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *Alpha16) Opaque() bool { if p.Rect.Empty() { return true @@ -704,7 +704,7 @@ func (p *Gray) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *Gray) Opaque() bool { return true } @@ -782,7 +782,7 @@ func (p *Gray16) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *Gray16) Opaque() bool { return true } @@ -873,7 +873,7 @@ func (p *Paletted) SubImage(r Rectangle) Image { } } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (p *Paletted) Opaque() bool { var present [256]bool i0, i1 := 0, p.Rect.Dx() diff --git a/libgo/go/image/jpeg/dct_test.go b/libgo/go/image/jpeg/dct_test.go index 7389f7e..845e758 100644 --- a/libgo/go/image/jpeg/dct_test.go +++ b/libgo/go/image/jpeg/dct_test.go @@ -90,7 +90,7 @@ func TestDCT(t *testing.T) { } } -// differ returns whether any pair-wise elements in b0 and b1 differ by 2 or +// differ reports whether any pair-wise elements in b0 and b1 differ by 2 or // more. That tolerance is because there isn't a single definitive decoding of // a given JPEG image, even before the YCbCr to RGB conversion; implementations // can have different IDCT rounding errors. diff --git a/libgo/go/image/jpeg/reader.go b/libgo/go/image/jpeg/reader.go index 862d8dc..356d562 100644 --- a/libgo/go/image/jpeg/reader.go +++ b/libgo/go/image/jpeg/reader.go @@ -174,10 +174,10 @@ func (d *decoder) processSOF(n int) error { // values for the Cr and Cb components must be (1, 1). if i == 0 { if hv != 0x11 && hv != 0x21 && hv != 0x22 && hv != 0x12 { - return UnsupportedError("luma downsample ratio") + return UnsupportedError("luma/chroma downsample ratio") } } else if hv != 0x11 { - return UnsupportedError("chroma downsample ratio") + return UnsupportedError("luma/chroma downsample ratio") } } return nil diff --git a/libgo/go/image/names.go b/libgo/go/image/names.go index 04ee2cf..8985f49 100644 --- a/libgo/go/image/names.go +++ b/libgo/go/image/names.go @@ -41,7 +41,7 @@ func (c *Uniform) Bounds() Rectangle { return Rectangle{Point{-1e9, -1e9}, Point func (c *Uniform) At(x, y int) color.Color { return c.C } -// Opaque scans the entire image and returns whether or not it is fully opaque. +// Opaque scans the entire image and reports whether it is fully opaque. func (c *Uniform) Opaque() bool { _, _, _, a := c.C.RGBA() return a == 0xffff diff --git a/libgo/go/io/io.go b/libgo/go/io/io.go index ec2cd60..f7073ff 100644 --- a/libgo/go/io/io.go +++ b/libgo/go/io/io.go @@ -91,10 +91,14 @@ type Closer interface { // Seek sets the offset for the next Read or Write to offset, // interpreted according to whence: 0 means relative to the origin of // the file, 1 means relative to the current offset, and 2 means -// relative to the end. Seek returns the new offset and an Error, if +// relative to the end. Seek returns the new offset and an error, if // any. +// +// Seeking to a negative offset is an error. Seeking to any positive +// offset is legal, but the behavior of subsequent I/O operations on +// the underlying object is implementation-dependent. type Seeker interface { - Seek(offset int64, whence int) (ret int64, err error) + Seek(offset int64, whence int) (int64, error) } // ReadWriter is the interface that groups the basic Read and Write methods. @@ -329,20 +333,20 @@ func CopyN(dst Writer, src Reader, n int64) (written int64, err error) { // Because Copy is defined to read from src until EOF, it does // not treat an EOF from Read as an error to be reported. // -// If dst implements the ReaderFrom interface, -// the copy is implemented by calling dst.ReadFrom(src). -// Otherwise, if src implements the WriterTo interface, +// If src implements the WriterTo interface, // the copy is implemented by calling src.WriteTo(dst). +// Otherwise, if dst implements the ReaderFrom interface, +// the copy is implemented by calling dst.ReadFrom(src). func Copy(dst Writer, src Reader) (written int64, err error) { - // If the writer has a ReadFrom method, use it to do the copy. + // If the reader has a WriteTo method, use it to do the copy. // Avoids an allocation and a copy. - if rt, ok := dst.(ReaderFrom); ok { - return rt.ReadFrom(src) - } - // Similarly, if the reader has a WriteTo method, use it to do the copy. if wt, ok := src.(WriterTo); ok { return wt.WriteTo(dst) } + // Similarly, if the writer has a ReadFrom method, use it to do the copy. + if rt, ok := dst.(ReaderFrom); ok { + return rt.ReadFrom(src) + } buf := make([]byte, 32*1024) for { nr, er := src.Read(buf) @@ -426,7 +430,7 @@ func (s *SectionReader) Read(p []byte) (n int, err error) { var errWhence = errors.New("Seek: invalid whence") var errOffset = errors.New("Seek: invalid offset") -func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err error) { +func (s *SectionReader) Seek(offset int64, whence int) (int64, error) { switch whence { default: return 0, errWhence @@ -437,7 +441,7 @@ func (s *SectionReader) Seek(offset int64, whence int) (ret int64, err error) { case 2: offset += s.limit } - if offset < s.base || offset > s.limit { + if offset < s.base { return 0, errOffset } s.off = offset diff --git a/libgo/go/io/io_test.go b/libgo/go/io/io_test.go index 1bc451e..bd7a82f 100644 --- a/libgo/go/io/io_test.go +++ b/libgo/go/io/io_test.go @@ -52,6 +52,32 @@ func TestCopyWriteTo(t *testing.T) { } } +// Version of bytes.Buffer that checks whether WriteTo was called or not +type writeToChecker struct { + bytes.Buffer + writeToCalled bool +} + +func (wt *writeToChecker) WriteTo(w Writer) (int64, error) { + wt.writeToCalled = true + return wt.Buffer.WriteTo(w) +} + +// It's preferable to choose WriterTo over ReaderFrom, since a WriterTo can issue one large write, +// while the ReaderFrom must read until EOF, potentially allocating when running out of buffer. +// Make sure that we choose WriterTo when both are implemented. +func TestCopyPriority(t *testing.T) { + rb := new(writeToChecker) + wb := new(bytes.Buffer) + rb.WriteString("hello, world.") + Copy(wb, rb) + if wb.String() != "hello, world." { + t.Errorf("Copy did not work properly") + } else if !rb.writeToCalled { + t.Errorf("WriteTo was not prioritized over ReadFrom") + } +} + func TestCopyN(t *testing.T) { rb := new(Buffer) wb := new(Buffer) @@ -234,7 +260,7 @@ func TestTeeReader(t *testing.T) { } } -func TestSectionReader_ReadAt(tst *testing.T) { +func TestSectionReader_ReadAt(t *testing.T) { dat := "a long sample data, 1234567890" tests := []struct { data string @@ -256,12 +282,40 @@ func TestSectionReader_ReadAt(tst *testing.T) { {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 - 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: nil}, {data: dat, off: 3, n: len(dat) / 2, bufLen: len(dat)/2 + 2, at: 2, exp: dat[5 : 5+len(dat)/2-2], err: EOF}, } - for i, t := range tests { - r := strings.NewReader(t.data) - s := NewSectionReader(r, int64(t.off), int64(t.n)) - buf := make([]byte, t.bufLen) - if n, err := s.ReadAt(buf, int64(t.at)); n != len(t.exp) || string(buf[:n]) != t.exp || err != t.err { - tst.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, t.at, buf[:n], err, t.exp, t.err) + for i, tt := range tests { + r := strings.NewReader(tt.data) + s := NewSectionReader(r, int64(tt.off), int64(tt.n)) + buf := make([]byte, tt.bufLen) + if n, err := s.ReadAt(buf, int64(tt.at)); n != len(tt.exp) || string(buf[:n]) != tt.exp || err != tt.err { + t.Fatalf("%d: ReadAt(%d) = %q, %v; expected %q, %v", i, tt.at, buf[:n], err, tt.exp, tt.err) } } } + +func TestSectionReader_Seek(t *testing.T) { + // Verifies that NewSectionReader's Seeker behaves like bytes.NewReader (which is like strings.NewReader) + br := bytes.NewReader([]byte("foo")) + sr := NewSectionReader(br, 0, int64(len("foo"))) + + for whence := 0; whence <= 2; whence++ { + for offset := int64(-3); offset <= 4; offset++ { + brOff, brErr := br.Seek(offset, whence) + srOff, srErr := sr.Seek(offset, whence) + if (brErr != nil) != (srErr != nil) || brOff != srOff { + t.Errorf("For whence %d, offset %d: bytes.Reader.Seek = (%v, %v) != SectionReader.Seek = (%v, %v)", + whence, offset, brOff, brErr, srErr, srOff) + } + } + } + + // And verify we can just seek past the end and get an EOF + got, err := sr.Seek(100, 0) + if err != nil || got != 100 { + t.Errorf("Seek = %v, %v; want 100, nil", got, err) + } + + n, err := sr.Read(make([]byte, 10)) + if n != 0 || err != EOF { + t.Errorf("Read = %v, %v; want 0, EOF", n, err) + } +} diff --git a/libgo/go/io/ioutil/ioutil.go b/libgo/go/io/ioutil/ioutil.go index 6b395c6..b2508b7 100644 --- a/libgo/go/io/ioutil/ioutil.go +++ b/libgo/go/io/ioutil/ioutil.go @@ -78,10 +78,12 @@ func WriteFile(filename string, data []byte, perm os.FileMode) error { return err } n, err := f.Write(data) - f.Close() if err == nil && n < len(data) { err = io.ErrShortWrite } + if err1 := f.Close(); err == nil { + err = err1 + } return err } @@ -130,6 +132,10 @@ func (devNull) Write(p []byte) (int, error) { return len(p), nil } +func (devNull) WriteString(s string) (int, error) { + return len(s), nil +} + func (devNull) ReadFrom(r io.Reader) (n int64, err error) { buf := blackHole() defer blackHolePut(buf) diff --git a/libgo/go/io/pipe.go b/libgo/go/io/pipe.go index f3f0f17..f65354a 100644 --- a/libgo/go/io/pipe.go +++ b/libgo/go/io/pipe.go @@ -74,6 +74,10 @@ func (p *pipe) write(b []byte) (n int, err error) { p.l.Lock() defer p.l.Unlock() + if p.werr != nil { + err = ErrClosedPipe + return + } p.data = b p.rwait.Signal() for { diff --git a/libgo/go/io/pipe_test.go b/libgo/go/io/pipe_test.go index 7718151..b16e653 100644 --- a/libgo/go/io/pipe_test.go +++ b/libgo/go/io/pipe_test.go @@ -268,3 +268,35 @@ func TestWriteNil(t *testing.T) { ReadFull(r, b[0:2]) r.Close() } + +func TestWriteAfterWriterClose(t *testing.T) { + r, w := Pipe() + + done := make(chan bool) + var writeErr error + go func() { + _, err := w.Write([]byte("hello")) + if err != nil { + t.Errorf("got error: %q; expected none", err) + } + w.Close() + _, writeErr = w.Write([]byte("world")) + done <- true + }() + + buf := make([]byte, 100) + var result string + n, err := ReadFull(r, buf) + if err != nil && err != ErrUnexpectedEOF { + t.Fatalf("got: %q; want: %q", err, ErrUnexpectedEOF) + } + result = string(buf[0:n]) + <-done + + if result != "hello" { + t.Errorf("got: %q; want: %q", result, "hello") + } + if writeErr != ErrClosedPipe { + t.Errorf("got: %q; want: %q", writeErr, ErrClosedPipe) + } +} diff --git a/libgo/go/log/syslog/syslog.go b/libgo/go/log/syslog/syslog.go index 20acab8..0cbfa90 100644 --- a/libgo/go/log/syslog/syslog.go +++ b/libgo/go/log/syslog/syslog.go @@ -91,13 +91,20 @@ type Writer struct { conn serverConn } +// This interface and the separate syslog_unix.go file exist for +// Solaris support as implemented by gccgo. On Solaris you can not +// simply open a TCP connection to the syslog daemon. The gccgo +// sources have a syslog_solaris.go file that implements unixSyslog to +// return a type that satisfies this interface and simply calls the C +// library syslog function. type serverConn interface { writeString(p Priority, hostname, tag, s, nl string) error close() error } type netConn struct { - conn net.Conn + local bool + conn net.Conn } // New establishes a new connection to the system log daemon. Each @@ -157,7 +164,7 @@ func (w *Writer) connect() (err error) { var c net.Conn c, err = net.Dial(w.network, w.raddr) if err == nil { - w.conn = netConn{c} + w.conn = &netConn{conn: c} if w.hostname == "" { w.hostname = c.LocalAddr().String() } @@ -212,7 +219,7 @@ func (w *Writer) Err(m string) (err error) { return err } -// Wanring logs a message with severity LOG_WARNING, ignoring the +// Warning logs a message with severity LOG_WARNING, ignoring the // severity passed to New. func (w *Writer) Warning(m string) (err error) { _, err = w.writeAndRetry(LOG_WARNING, m) @@ -266,11 +273,27 @@ func (w *Writer) write(p Priority, msg string) (int, error) { nl = "\n" } - w.conn.writeString(p, w.hostname, w.tag, msg, nl) + err := w.conn.writeString(p, w.hostname, w.tag, msg, nl) + if err != nil { + return 0, err + } + // Note: return the length of the input, not the number of + // bytes printed by Fprintf, because this must behave like + // an io.Writer. return len(msg), nil } -func (n netConn) writeString(p Priority, hostname, tag, msg, nl string) error { +func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error { + if n.local { + // Compared to the network form below, the changes are: + // 1. Use time.Stamp instead of time.RFC3339. + // 2. Drop the hostname field from the Fprintf. + timestamp := time.Now().Format(time.Stamp) + _, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s", + p, timestamp, + tag, os.Getpid(), msg, nl) + return err + } timestamp := time.Now().Format(time.RFC3339) _, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s", p, timestamp, hostname, @@ -278,7 +301,7 @@ func (n netConn) writeString(p Priority, hostname, tag, msg, nl string) error { return err } -func (n netConn) close() error { +func (n *netConn) close() error { return n.conn.Close() } diff --git a/libgo/go/log/syslog/syslog_test.go b/libgo/go/log/syslog/syslog_test.go index ec45251..760a5c7 100644 --- a/libgo/go/log/syslog/syslog_test.go +++ b/libgo/go/log/syslog/syslog_test.go @@ -122,7 +122,9 @@ func TestWithSimulated(t *testing.T) { for _, tr := range transport { done := make(chan string) - addr, _, _ := startServer(tr, "", done) + addr, sock, srvWG := startServer(tr, "", done) + defer srvWG.Wait() + defer sock.Close() if tr == "unix" || tr == "unixgram" { defer os.Remove(addr) } @@ -142,7 +144,8 @@ func TestWithSimulated(t *testing.T) { func TestFlap(t *testing.T) { net := "unix" done := make(chan string) - addr, sock, _ := startServer(net, "", done) + addr, sock, srvWG := startServer(net, "", done) + defer srvWG.Wait() defer os.Remove(addr) defer sock.Close() @@ -158,7 +161,8 @@ func TestFlap(t *testing.T) { check(t, msg, <-done) // restart the server - _, sock2, _ := startServer(net, addr, done) + _, sock2, srvWG2 := startServer(net, addr, done) + defer srvWG2.Wait() defer sock2.Close() // and try retransmitting @@ -249,12 +253,14 @@ func TestWrite(t *testing.T) { } else { for _, test := range tests { done := make(chan string) - addr, sock, _ := startServer("udp", "", done) + addr, sock, srvWG := startServer("udp", "", done) + defer srvWG.Wait() defer sock.Close() l, err := Dial("udp", addr, test.pri, test.pre) if err != nil { t.Fatalf("syslog.Dial() failed: %v", err) } + defer l.Close() _, err = io.WriteString(l, test.msg) if err != nil { t.Fatalf("WriteString() failed: %v", err) @@ -271,7 +277,8 @@ func TestWrite(t *testing.T) { } func TestConcurrentWrite(t *testing.T) { - addr, sock, _ := startServer("udp", "", make(chan string)) + addr, sock, srvWG := startServer("udp", "", make(chan string, 1)) + defer srvWG.Wait() defer sock.Close() w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?") if err != nil { @@ -281,12 +288,12 @@ func TestConcurrentWrite(t *testing.T) { for i := 0; i < 10; i++ { wg.Add(1) go func() { + defer wg.Done() err := w.Info("test") if err != nil { t.Errorf("Info() failed: %v", err) return } - wg.Done() }() } wg.Wait() @@ -296,8 +303,10 @@ func TestConcurrentReconnect(t *testing.T) { crashy = true defer func() { crashy = false }() + const N = 10 + const M = 100 net := "unix" - done := make(chan string) + done := make(chan string, N*M) addr, sock, srvWG := startServer(net, "", done) defer os.Remove(addr) @@ -310,7 +319,7 @@ func TestConcurrentReconnect(t *testing.T) { // we are looking for 500 out of 1000 events // here because lots of log messages are lost // in buffers (kernel and/or bufio) - if ct > 500 { + if ct > N*M/2 { break } } @@ -318,21 +327,22 @@ func TestConcurrentReconnect(t *testing.T) { }() var wg sync.WaitGroup - for i := 0; i < 10; i++ { - wg.Add(1) + wg.Add(N) + for i := 0; i < N; i++ { go func() { + defer wg.Done() w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag") if err != nil { t.Fatalf("syslog.Dial() failed: %v", err) } - for i := 0; i < 100; i++ { + defer w.Close() + for i := 0; i < M; i++ { err := w.Info("test") if err != nil { t.Errorf("Info() failed: %v", err) return } } - wg.Done() }() } wg.Wait() diff --git a/libgo/go/log/syslog/syslog_unix.go b/libgo/go/log/syslog/syslog_unix.go index 1716d60..28a294a 100644 --- a/libgo/go/log/syslog/syslog_unix.go +++ b/libgo/go/log/syslog/syslog_unix.go @@ -23,7 +23,7 @@ func unixSyslog() (conn serverConn, err error) { if err != nil { continue } else { - return netConn{conn}, nil + return &netConn{conn: conn, local: true}, nil } } } diff --git a/libgo/go/math/asin.go b/libgo/go/math/asin.go index 0d4fa9e..46a5fe9 100644 --- a/libgo/go/math/asin.go +++ b/libgo/go/math/asin.go @@ -11,7 +11,7 @@ package math after appropriate range reduction. */ -// Asin returns the arcsine of x. +// Asin returns the arcsine, in radians, of x. // // Special cases are: // Asin(±0) = ±0 @@ -50,7 +50,7 @@ func asin(x float64) float64 { return temp } -// Acos returns the arccosine of x. +// Acos returns the arccosine, in radians, of x. // // Special case is: // Acos(x) = NaN if x < -1 or x > 1 diff --git a/libgo/go/math/atan.go b/libgo/go/math/atan.go index 986ba06..d942bce 100644 --- a/libgo/go/math/atan.go +++ b/libgo/go/math/atan.go @@ -87,7 +87,7 @@ func satan(x float64) float64 { return Pi/4 + xatan((x-1)/(x+1)) + 0.5*Morebits } -// Atan returns the arctangent of x. +// Atan returns the arctangent, in radians, of x. // // Special cases are: // Atan(±0) = ±0 diff --git a/libgo/go/math/big/int.go b/libgo/go/math/big/int.go index d1b5602..7bbb152 100644 --- a/libgo/go/math/big/int.go +++ b/libgo/go/math/big/int.go @@ -563,13 +563,13 @@ func (z *Int) SetBytes(buf []byte) *Int { return z } -// Bytes returns the absolute value of z as a big-endian byte slice. +// Bytes returns the absolute value of x as a big-endian byte slice. func (x *Int) Bytes() []byte { buf := make([]byte, len(x.abs)*_S) return buf[x.abs.bytes(buf):] } -// BitLen returns the length of the absolute value of z in bits. +// BitLen returns the length of the absolute value of x in bits. // The bit length of 0 is 0. func (x *Int) BitLen() int { return x.abs.bitLen() @@ -703,14 +703,15 @@ func (z *Int) binaryGCD(a, b *Int) *Int { // reduce t t.Rsh(t, t.abs.trailingZeroBits()) if t.neg { - v.Neg(t) + v, t = t, v + v.neg = len(v.abs) > 0 && !v.neg // 0 has no sign } else { - u.Set(t) + u, t = t, u } t.Sub(u, v) } - return u.Lsh(u, k) + return z.Lsh(u, k) } // ProbablyPrime performs n Miller-Rabin tests to check whether x is prime. @@ -951,6 +952,9 @@ const intGobVersion byte = 1 // GobEncode implements the gob.GobEncoder interface. func (x *Int) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } buf := make([]byte, 1+len(x.abs)*_S) // extra byte for version and sign bit i := x.abs.bytes(buf) - 1 // i >= 0 b := intGobVersion << 1 // make space for sign bit @@ -964,7 +968,9 @@ func (x *Int) GobEncode() ([]byte, error) { // GobDecode implements the gob.GobDecoder interface. func (z *Int) GobDecode(buf []byte) error { if len(buf) == 0 { - return errors.New("Int.GobDecode: no data") + // Other side sent a nil or default value. + *z = Int{} + return nil } b := buf[0] if b>>1 != intGobVersion { diff --git a/libgo/go/math/big/int_test.go b/libgo/go/math/big/int_test.go index 6c981e7..87b975d 100644 --- a/libgo/go/math/big/int_test.go +++ b/libgo/go/math/big/int_test.go @@ -89,7 +89,7 @@ func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) { var z Int f(&z, a.x, a.y) if !isNormalized(&z) { - t.Errorf("%s%v is not normalized", z, msg) + t.Errorf("%s%v is not normalized", msg, z) } if (&z).Cmp(a.z) != 0 { t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z) @@ -1484,6 +1484,32 @@ func TestIntGobEncoding(t *testing.T) { } } +// Sending a nil Int pointer (inside a slice) on a round trip through gob should yield a zero. +// TODO: top-level nils. +func TestGobEncodingNilIntInSlice(t *testing.T) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + dec := gob.NewDecoder(buf) + + var in = make([]*Int, 1) + err := enc.Encode(&in) + if err != nil { + t.Errorf("gob encode failed: %q", err) + } + var out []*Int + err = dec.Decode(&out) + if err != nil { + t.Fatalf("gob decode failed: %q", err) + } + if len(out) != 1 { + t.Fatalf("wrong len; want 1 got %d", len(out)) + } + var zero Int + if out[0].Cmp(&zero) != 0 { + t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out) + } +} + func TestIntJSONEncoding(t *testing.T) { for _, test := range encodingTests { var tx Int diff --git a/libgo/go/math/big/nat_test.go b/libgo/go/math/big/nat_test.go index 2dd7bf6..1d4dfe8 100644 --- a/libgo/go/math/big/nat_test.go +++ b/libgo/go/math/big/nat_test.go @@ -685,7 +685,7 @@ func runModWTests(t *testing.T, tests []modWTest) { r := in.abs.modW(d.abs[0]) if r != out.abs[0] { - t.Errorf("#%d failed: got %s want %s", i, r, out) + t.Errorf("#%d failed: got %d want %s", i, r, out) } } } diff --git a/libgo/go/math/big/rat.go b/libgo/go/math/big/rat.go index 75d044f..7faee61a 100644 --- a/libgo/go/math/big/rat.go +++ b/libgo/go/math/big/rat.go @@ -164,8 +164,9 @@ func quotToFloat(a, b nat) (f float64, exact bool) { } // Float64 returns the nearest float64 value for x and a bool indicating -// whether f represents x exactly. The sign of f always matches the sign -// of x, even if f == 0. +// whether f represents x exactly. If the magnitude of x is too large to +// be represented by a float64, f is an infinity and exact is false. +// The sign of f always matches the sign of x, even if f == 0. func (x *Rat) Float64() (f float64, exact bool) { b := x.b.abs if len(b) == 0 { @@ -545,6 +546,9 @@ const ratGobVersion byte = 1 // GobEncode implements the gob.GobEncoder interface. func (x *Rat) GobEncode() ([]byte, error) { + if x == nil { + return nil, nil + } buf := make([]byte, 1+4+(len(x.a.abs)+len(x.b.abs))*_S) // extra bytes for version and sign bit (1), and numerator length (4) i := x.b.abs.bytes(buf) j := x.a.abs.bytes(buf[0:i]) @@ -566,7 +570,9 @@ func (x *Rat) GobEncode() ([]byte, error) { // GobDecode implements the gob.GobDecoder interface. func (z *Rat) GobDecode(buf []byte) error { if len(buf) == 0 { - return errors.New("Rat.GobDecode: no data") + // Other side sent a nil or default value. + *z = Rat{} + return nil } b := buf[0] if b>>1 != ratGobVersion { diff --git a/libgo/go/math/big/rat_test.go b/libgo/go/math/big/rat_test.go index 1c2c642..0d43263 100644 --- a/libgo/go/math/big/rat_test.go +++ b/libgo/go/math/big/rat_test.go @@ -407,6 +407,32 @@ func TestRatGobEncoding(t *testing.T) { } } +// Sending a nil Rat pointer (inside a slice) on a round trip through gob should yield a zero. +// TODO: top-level nils. +func TestGobEncodingNilRatInSlice(t *testing.T) { + buf := new(bytes.Buffer) + enc := gob.NewEncoder(buf) + dec := gob.NewDecoder(buf) + + var in = make([]*Rat, 1) + err := enc.Encode(&in) + if err != nil { + t.Errorf("gob encode failed: %q", err) + } + var out []*Rat + err = dec.Decode(&out) + if err != nil { + t.Fatalf("gob decode failed: %q", err) + } + if len(out) != 1 { + t.Fatalf("wrong len; want 1 got %d", len(out)) + } + var zero Rat + if out[0].Cmp(&zero) != 0 { + t.Errorf("transmission of (*Int)(nill) failed: got %s want 0", out) + } +} + func TestIssue2379(t *testing.T) { // 1) no aliasing q := NewRat(3, 2) diff --git a/libgo/go/math/bits.go b/libgo/go/math/bits.go index 0df0b1c..d85ee9c 100644 --- a/libgo/go/math/bits.go +++ b/libgo/go/math/bits.go @@ -27,7 +27,7 @@ func Inf(sign int) float64 { // NaN returns an IEEE 754 ``not-a-number'' value. func NaN() float64 { return Float64frombits(uvnan) } -// IsNaN returns whether f is an IEEE 754 ``not-a-number'' value. +// IsNaN reports whether f is an IEEE 754 ``not-a-number'' value. func IsNaN(f float64) (is bool) { // IEEE 754 says that only NaNs satisfy f != f. // To avoid the floating-point hardware, could use: @@ -36,10 +36,10 @@ func IsNaN(f float64) (is bool) { return f != f } -// IsInf returns whether f is an infinity, according to sign. -// If sign > 0, IsInf returns whether f is positive infinity. -// If sign < 0, IsInf returns whether f is negative infinity. -// If sign == 0, IsInf returns whether f is either infinity. +// IsInf reports whether f is an infinity, according to sign. +// If sign > 0, IsInf reports whether f is positive infinity. +// If sign < 0, IsInf reports whether f is negative infinity. +// If sign == 0, IsInf reports whether f is either infinity. func IsInf(f float64, sign int) bool { // Test for infinity by comparing against maximum float. // To avoid the floating-point hardware, could use: diff --git a/libgo/go/math/rand/rand.go b/libgo/go/math/rand/rand.go index 94f84a8..2157cdb 100644 --- a/libgo/go/math/rand/rand.go +++ b/libgo/go/math/rand/rand.go @@ -3,6 +3,12 @@ // license that can be found in the LICENSE file. // Package rand implements pseudo-random number generators. +// +// Random numbers are generated by a Source. Top-level functions, such as +// Float64 and Int, use a default shared Source that produces a deterministic +// sequence of values each time a program is run. Use the Seed function to +// initialize the default Source if different behavior is required for each run. +// The default Source is safe for concurrent use by multiple goroutines. package rand import "sync" @@ -113,47 +119,57 @@ func (r *Rand) Perm(n int) []int { var globalRand = New(&lockedSource{src: NewSource(1)}) -// Seed uses the provided seed value to initialize the generator to a +// Seed uses the provided seed value to initialize the default Source to a // deterministic state. If Seed is not called, the generator behaves as // if seeded by Seed(1). func Seed(seed int64) { globalRand.Seed(seed) } -// Int63 returns a non-negative pseudo-random 63-bit integer as an int64. +// Int63 returns a non-negative pseudo-random 63-bit integer as an int64 +// from the default Source. func Int63() int64 { return globalRand.Int63() } -// Uint32 returns a pseudo-random 32-bit value as a uint32. +// Uint32 returns a pseudo-random 32-bit value as a uint32 +// from the default Source. func Uint32() uint32 { return globalRand.Uint32() } -// Int31 returns a non-negative pseudo-random 31-bit integer as an int32. +// Int31 returns a non-negative pseudo-random 31-bit integer as an int32 +// from the default Source. func Int31() int32 { return globalRand.Int31() } -// Int returns a non-negative pseudo-random int. +// Int returns a non-negative pseudo-random int from the default Source. func Int() int { return globalRand.Int() } -// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n). +// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n) +// from the default Source. // It panics if n <= 0. func Int63n(n int64) int64 { return globalRand.Int63n(n) } -// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n). +// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n) +// from the default Source. // It panics if n <= 0. func Int31n(n int32) int32 { return globalRand.Int31n(n) } -// Intn returns, as an int, a non-negative pseudo-random number in [0,n). +// Intn returns, as an int, a non-negative pseudo-random number in [0,n) +// from the default Source. // It panics if n <= 0. func Intn(n int) int { return globalRand.Intn(n) } -// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0). +// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) +// from the default Source. func Float64() float64 { return globalRand.Float64() } -// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0). +// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0) +// from the default Source. func Float32() float32 { return globalRand.Float32() } -// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n). +// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n) +// from the default Source. func Perm(n int) []int { return globalRand.Perm(n) } // NormFloat64 returns a normally distributed float64 in the range // [-math.MaxFloat64, +math.MaxFloat64] with -// standard normal distribution (mean = 0, stddev = 1). +// standard normal distribution (mean = 0, stddev = 1) +// from the default Source. // To produce a different normal distribution, callers can // adjust the output using: // @@ -163,7 +179,7 @@ func NormFloat64() float64 { return globalRand.NormFloat64() } // ExpFloat64 returns an exponentially distributed float64 in the range // (0, +math.MaxFloat64] with an exponential distribution whose rate parameter -// (lambda) is 1 and whose mean is 1/lambda (1). +// (lambda) is 1 and whose mean is 1/lambda (1) from the default Source. // To produce a distribution with a different rate parameter, // callers can adjust the output using: // diff --git a/libgo/go/math/sin.go b/libgo/go/math/sin.go index 13fe408..1c5491f 100644 --- a/libgo/go/math/sin.go +++ b/libgo/go/math/sin.go @@ -109,7 +109,7 @@ var _cos = [...]float64{ 4.16666666666665929218E-2, // 0x3fa555555555554b } -// Cos returns the cosine of x. +// Cos returns the cosine of the radian argument x. // // Special cases are: // Cos(±Inf) = NaN @@ -171,7 +171,7 @@ func cos(x float64) float64 { return y } -// Sin returns the sine of x. +// Sin returns the sine of the radian argument x. // // Special cases are: // Sin(±0) = ±0 diff --git a/libgo/go/math/tan.go b/libgo/go/math/tan.go index 640f404..e544b27 100644 --- a/libgo/go/math/tan.go +++ b/libgo/go/math/tan.go @@ -73,7 +73,7 @@ var _tanQ = [...]float64{ -5.38695755929454629881E7, //0xc189afe03cbe5a31 } -// Tan returns the tangent of x. +// Tan returns the tangent of the radian argument x. // // Special cases are: // Tan(±0) = ±0 diff --git a/libgo/go/mime/grammar.go b/libgo/go/mime/grammar.go index 09e941e..2347324 100644 --- a/libgo/go/mime/grammar.go +++ b/libgo/go/mime/grammar.go @@ -30,16 +30,3 @@ func isToken(s string) bool { } return strings.IndexFunc(s, isNotTokenChar) < 0 } - -// isQText returns true if rune is in 'qtext' as defined by RFC 822. -func isQText(r int) bool { - // CHAR = <any ASCII character> ; ( 0-177, 0.-127.) - // qtext = <any CHAR excepting <">, ; => may be folded - // "\" & CR, and including - // linear-white-space> - switch r { - case '"', '\\', '\r': - return false - } - return r < 0x80 -} diff --git a/libgo/go/mime/mediatype.go b/libgo/go/mime/mediatype.go index 8396c0a..608f759 100644 --- a/libgo/go/mime/mediatype.go +++ b/libgo/go/mime/mediatype.go @@ -47,7 +47,7 @@ func FormatMediaType(t string, param map[string]string) string { b.WriteByte('"') offset := 0 for index, character := range value { - if character == '"' || character == '\r' { + if character == '"' || character == '\\' { b.WriteString(value[offset:index]) offset = index b.WriteByte('\\') diff --git a/libgo/go/mime/mediatype_test.go b/libgo/go/mime/mediatype_test.go index e41ead2..2951144 100644 --- a/libgo/go/mime/mediatype_test.go +++ b/libgo/go/mime/mediatype_test.go @@ -282,8 +282,17 @@ type formatTest struct { var formatTests = []formatTest{ {"noslash", nil, ""}, + {"foo bar/baz", nil, ""}, + {"foo/bar baz", nil, ""}, {"foo/BAR", nil, "foo/bar"}, {"foo/BAR", map[string]string{"X": "Y"}, "foo/bar; x=Y"}, + {"foo/BAR", map[string]string{"space": "With space"}, `foo/bar; space="With space"`}, + {"foo/BAR", map[string]string{"quote": `With "quote`}, `foo/bar; quote="With \"quote"`}, + {"foo/BAR", map[string]string{"bslash": `With \backslash`}, `foo/bar; bslash="With \\backslash"`}, + {"foo/BAR", map[string]string{"both": `With \backslash and "quote`}, `foo/bar; both="With \\backslash and \"quote"`}, + {"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: ä"}, ""}, } func TestFormatMediaType(t *testing.T) { diff --git a/libgo/go/mime/multipart/multipart.go b/libgo/go/mime/multipart/multipart.go index 2c862a6..2b4f5b4 100644 --- a/libgo/go/mime/multipart/multipart.go +++ b/libgo/go/mime/multipart/multipart.go @@ -272,7 +272,7 @@ func (r *Reader) NextPart() (*Part, error) { } } -// isFinalBoundary returns whether line is the final boundary line +// isFinalBoundary reports whether line is the final boundary line // indicating that all parts are over. // It matches `^--boundary--[ \t]*(\r\n)?$` func (mr *Reader) isFinalBoundary(line []byte) bool { @@ -307,8 +307,8 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) { return bytes.Equal(rest, mr.nl) } -// peekBufferIsEmptyPart returns whether the provided peek-ahead -// buffer represents an empty part. This is only called if we've not +// peekBufferIsEmptyPart reports whether the provided peek-ahead +// buffer represents an empty part. It is called only if we've not // already read any bytes in this part and checks for the case of MIME // software not writing the \r\n on empty parts. Some does, some // doesn't. diff --git a/libgo/go/mime/type_plan9.go b/libgo/go/mime/type_plan9.go new file mode 100644 index 0000000..b8f0511 --- /dev/null +++ b/libgo/go/mime/type_plan9.go @@ -0,0 +1,53 @@ +// 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 mime + +import ( + "bufio" + "os" + "strings" +) + +var typeFiles = []string{ + "/sys/lib/mimetypes", +} + +func loadMimeFile(filename string) { + f, err := os.Open(filename) + if err != nil { + return + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + fields := strings.Fields(scanner.Text()) + if len(fields) <= 2 || fields[0][0] != '.' { + continue + } + if fields[1] == "-" || fields[2] == "-" { + continue + } + setExtensionType(fields[0], fields[1]+"/"+fields[2]) + } + if err := scanner.Err(); err != nil { + panic(err) + } +} + +func initMime() { + for _, filename := range typeFiles { + loadMimeFile(filename) + } +} + +func initMimeForTests() map[string]string { + typeFiles = []string{"testdata/test.types.plan9"} + return map[string]string{ + ".t1": "application/test", + ".t2": "text/test; charset=utf-8", + ".png": "image/png", + } +} diff --git a/libgo/go/mime/type_unix.go b/libgo/go/mime/type_unix.go index 857a0ab..713e301 100644 --- a/libgo/go/mime/type_unix.go +++ b/libgo/go/mime/type_unix.go @@ -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 freebsd linux netbsd openbsd plan9 +// +build darwin dragonfly freebsd linux netbsd openbsd package mime diff --git a/libgo/go/net/cgo_bsd.go b/libgo/go/net/cgo_bsd.go index 27c3e9a..3852fc2 100644 --- a/libgo/go/net/cgo_bsd.go +++ b/libgo/go/net/cgo_bsd.go @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd +// +build !netgo +// +build darwin dragonfly freebsd package net diff --git a/libgo/go/net/cgo_linux.go b/libgo/go/net/cgo_linux.go index 650575c..77522f9 100644 --- a/libgo/go/net/cgo_linux.go +++ b/libgo/go/net/cgo_linux.go @@ -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 cgo,!netgo + package net /* diff --git a/libgo/go/net/cgo_netbsd.go b/libgo/go/net/cgo_netbsd.go index 27334af..3c13103 100644 --- a/libgo/go/net/cgo_netbsd.go +++ b/libgo/go/net/cgo_netbsd.go @@ -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 cgo,!netgo + package net /* diff --git a/libgo/go/net/cgo_openbsd.go b/libgo/go/net/cgo_openbsd.go index aeaf8e5..09c5ad2 100644 --- a/libgo/go/net/cgo_openbsd.go +++ b/libgo/go/net/cgo_openbsd.go @@ -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 cgo,!netgo + package net /* diff --git a/libgo/go/net/cgo_stub.go b/libgo/go/net/cgo_stub.go index 52e57d7..f533c14 100644 --- a/libgo/go/net/cgo_stub.go +++ b/libgo/go/net/cgo_stub.go @@ -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 !cgo +// +build !cgo netgo // Stub cgo routines for systems that do not use cgo to do network lookups. diff --git a/libgo/go/net/cgo_unix.go b/libgo/go/net/cgo_unix.go index ce54d82..0abf434 100644 --- a/libgo/go/net/cgo_unix.go +++ b/libgo/go/net/cgo_unix.go @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build !netgo +// +build darwin dragonfly freebsd linux netbsd openbsd package net @@ -50,6 +51,9 @@ func cgoLookupHost(name string) (addrs []string, err error, completed bool) { } func cgoLookupPort(net, service string) (port int, err error, completed bool) { + acquireThread() + defer releaseThread() + var res *syscall.Addrinfo var hints syscall.Addrinfo @@ -99,6 +103,9 @@ func cgoLookupPort(net, service string) (port int, err error, completed bool) { } func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, completed bool) { + acquireThread() + defer releaseThread() + var res *syscall.Addrinfo var hints syscall.Addrinfo @@ -114,7 +121,18 @@ func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, complet if gerrno == syscall.EAI_NONAME { str = noSuchHost } else if gerrno == syscall.EAI_SYSTEM { - str = syscall.GetErrno().Error() + errno := syscall.GetErrno() + if errno == 0 { + // err should not be nil, but sometimes getaddrinfo returns + // gerrno == C.EAI_SYSTEM with err == nil on Linux. + // The report claims that it happens when we have too many + // open files, so use syscall.EMFILE (too many open files in system). + // Most system calls would return ENFILE (too many open files), + // so at the least EMFILE should be easy to recognize if this + // comes up again. golang.org/issue/6232. + errno = syscall.EMFILE + } + str = errno.Error() } else { str = bytePtrToString(libc_gai_strerror(gerrno)) } @@ -160,6 +178,9 @@ func cgoLookupCNAME(name string) (cname string, err error, completed bool) { } func copyIP(x IP) IP { + if len(x) < 16 { + return x.To16() + } y := make(IP, len(x)) copy(y, x) return y diff --git a/libgo/go/net/dial.go b/libgo/go/net/dial.go index b18d283..6304818 100644 --- a/libgo/go/net/dial.go +++ b/libgo/go/net/dial.go @@ -37,6 +37,13 @@ type Dialer struct { // network being dialed. // If nil, a local address is automatically chosen. LocalAddr Addr + + // DualStack allows a single dial to attempt to establish + // multiple IPv4 and IPv6 connections and to return the first + // established connection when the network is "tcp" and the + // destination is a host name that has multiple address family + // DNS records. + DualStack bool } // Return either now+Timeout or Deadline, whichever comes first. @@ -82,13 +89,13 @@ func parseNetwork(net string) (afnet string, proto int, err error) { return "", 0, UnknownNetworkError(net) } -func resolveAddr(op, net, addr string, deadline time.Time) (Addr, error) { +func resolveAddr(op, net, addr string, deadline time.Time) (netaddr, error) { afnet, _, err := parseNetwork(net) if err != nil { - return nil, &OpError{op, net, nil, err} + return nil, err } if op == "dial" && addr == "" { - return nil, &OpError{op, net, nil, errMissingAddress} + return nil, errMissingAddress } switch afnet { case "unix", "unixgram", "unixpacket": @@ -143,12 +150,74 @@ func DialTimeout(network, address string, timeout time.Duration) (Conn, error) { // See func Dial for a description of the network and address // parameters. func (d *Dialer) Dial(network, address string) (Conn, error) { - return resolveAndDial(network, address, d.LocalAddr, d.deadline()) + ra, err := resolveAddr("dial", network, address, d.deadline()) + if err != nil { + return nil, &OpError{Op: "dial", Net: network, Addr: nil, Err: err} + } + dialer := func(deadline time.Time) (Conn, error) { + return dialSingle(network, address, d.LocalAddr, ra.toAddr(), deadline) + } + if ras, ok := ra.(addrList); ok && d.DualStack && network == "tcp" { + dialer = func(deadline time.Time) (Conn, error) { + return dialMulti(network, address, d.LocalAddr, ras, deadline) + } + } + return dial(network, ra.toAddr(), dialer, d.deadline()) } -func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) { +// 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 +// error on the last attempt. +func dialMulti(net, addr string, la Addr, ras addrList, deadline time.Time) (Conn, error) { + type racer struct { + Conn + Addr + error + } + // Sig controls the flow of dial results on lane. It passes a + // token to the next racer and also indicates the end of flow + // by using closed channel. + sig := make(chan bool, 1) + lane := make(chan racer, 1) + for _, ra := range ras { + go func(ra Addr) { + c, err := dialSingle(net, addr, la, ra, deadline) + if _, ok := <-sig; ok { + lane <- racer{c, ra, err} + } else if err == nil { + // We have to return the resources + // that belong to the other + // connections here for avoiding + // unnecessary resource starvation. + c.Close() + } + }(ra.toAddr()) + } + defer close(sig) + var failAddr Addr + lastErr := errTimeout + nracers := len(ras) + for nracers > 0 { + sig <- true + select { + case racer := <-lane: + if racer.error == nil { + return racer.Conn, nil + } + failAddr = racer.Addr + lastErr = racer.error + nracers-- + } + } + return nil, &OpError{Op: "dial", Net: net, Addr: failAddr, Err: lastErr} +} + +// dialSingle attempts to establish and returns a single connection to +// the destination address. +func dialSingle(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) { if la != nil && la.Network() != ra.Network() { - return nil, &OpError{"dial", net, ra, errors.New("mismatched local addr type " + la.Network())} + return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: errors.New("mismatched local address type " + la.Network())} } switch ra := ra.(type) { case *TCPAddr: @@ -164,21 +233,14 @@ func dial(net, addr string, la, ra Addr, deadline time.Time) (c Conn, err error) la, _ := la.(*UnixAddr) c, err = dialUnix(net, la, ra, deadline) default: - err = &OpError{"dial", net + " " + addr, ra, UnknownNetworkError(net)} + return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: &AddrError{Err: "unexpected address type", Addr: addr}} } if err != nil { - return nil, err + return nil, err // c is non-nil interface containing nil pointer } - return -} - -type stringAddr struct { - net, addr string + return c, nil } -func (a stringAddr) Network() string { return a.net } -func (a stringAddr) String() string { return a.addr } - // Listen announces on the local network address laddr. // The network net must be a stream-oriented network: "tcp", "tcp4", // "tcp6", "unix" or "unixpacket". @@ -186,15 +248,21 @@ func (a stringAddr) String() string { return a.addr } func Listen(net, laddr string) (Listener, error) { la, err := resolveAddr("listen", net, laddr, noDeadline) if err != nil { - return nil, err + return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err} } - switch la := la.(type) { + var l Listener + switch la := la.toAddr().(type) { case *TCPAddr: - return ListenTCP(net, la) + l, err = ListenTCP(net, la) case *UnixAddr: - return ListenUnix(net, la) + l, err = ListenUnix(net, la) + default: + return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}} } - return nil, UnknownNetworkError(net) + if err != nil { + return nil, err // l is non-nil interface containing nil pointer + } + return l, nil } // ListenPacket announces on the local network address laddr. @@ -204,15 +272,21 @@ func Listen(net, laddr string) (Listener, error) { func ListenPacket(net, laddr string) (PacketConn, error) { la, err := resolveAddr("listen", net, laddr, noDeadline) if err != nil { - return nil, err + return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: err} } - switch la := la.(type) { + var l PacketConn + switch la := la.toAddr().(type) { case *UDPAddr: - return ListenUDP(net, la) + l, err = ListenUDP(net, la) case *IPAddr: - return ListenIP(net, la) + l, err = ListenIP(net, la) case *UnixAddr: - return ListenUnixgram(net, la) + l, err = ListenUnixgram(net, la) + default: + return nil, &OpError{Op: "listen", Net: net, Addr: la, Err: &AddrError{Err: "unexpected address type", Addr: laddr}} + } + if err != nil { + return nil, err // l is non-nil interface containing nil pointer } - return nil, UnknownNetworkError(net) + return l, nil } diff --git a/libgo/go/net/dial_gen.go b/libgo/go/net/dial_gen.go index 19f8681..ada6233 100644 --- a/libgo/go/net/dial_gen.go +++ b/libgo/go/net/dial_gen.go @@ -12,62 +12,35 @@ import ( var testingIssue5349 bool // used during tests -// resolveAndDialChannel is the simple pure-Go implementation of -// resolveAndDial, still used on operating systems where the deadline -// hasn't been pushed down into the pollserver. (Plan 9 and some old -// versions of Windows) -func resolveAndDialChannel(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) { +// dialChannel is the simple pure-Go implementation of dial, still +// used on operating systems where the deadline hasn't been pushed +// down into the pollserver. (Plan 9 and some old versions of Windows) +func dialChannel(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) { var timeout time.Duration if !deadline.IsZero() { timeout = deadline.Sub(time.Now()) } if timeout <= 0 { - ra, err := resolveAddr("dial", net, addr, noDeadline) - if err != nil { - return nil, err - } - return dial(net, addr, localAddr, ra, noDeadline) + return dialer(noDeadline) } t := time.NewTimer(timeout) defer t.Stop() - type pair struct { + type racer struct { Conn error } - ch := make(chan pair, 1) - resolvedAddr := make(chan Addr, 1) + ch := make(chan racer, 1) go func() { if testingIssue5349 { time.Sleep(time.Millisecond) } - ra, err := resolveAddr("dial", net, addr, noDeadline) - if err != nil { - ch <- pair{nil, err} - return - } - resolvedAddr <- ra // in case we need it for OpError - c, err := dial(net, addr, localAddr, ra, noDeadline) - ch <- pair{c, err} + c, err := dialer(noDeadline) + ch <- racer{c, err} }() select { case <-t.C: - // Try to use the real Addr in our OpError, if we resolved it - // before the timeout. Otherwise we just use stringAddr. - var ra Addr - select { - case a := <-resolvedAddr: - ra = a - default: - ra = &stringAddr{net, addr} - } - err := &OpError{ - Op: "dial", - Net: net, - Addr: ra, - Err: &timeoutError{}, - } - return nil, err - case p := <-ch: - return p.Conn, p.error + return nil, &OpError{Op: "dial", Net: net, Addr: ra, Err: errTimeout} + case racer := <-ch: + return racer.Conn, racer.error } } diff --git a/libgo/go/net/dial_test.go b/libgo/go/net/dial_test.go index 03a0bad..c7ffdd3 100644 --- a/libgo/go/net/dial_test.go +++ b/libgo/go/net/dial_test.go @@ -5,13 +5,17 @@ package net import ( + "bytes" "flag" "fmt" "io" "os" + "os/exec" "reflect" "regexp" "runtime" + "strconv" + "sync" "testing" "time" ) @@ -137,7 +141,7 @@ func TestSelfConnect(t *testing.T) { n = 1000 } switch runtime.GOOS { - case "darwin", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows": + case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd", "plan9", "solaris", "windows": // Non-Linux systems take a long time to figure // out that there is nothing listening on localhost. n = 100 @@ -314,6 +318,96 @@ func TestDialTimeoutFDLeak(t *testing.T) { } } +func numTCP() (ntcp, nopen, nclose int, err error) { + lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output() + if err != nil { + return 0, 0, 0, err + } + ntcp += bytes.Count(lsof, []byte("TCP")) + for _, state := range []string{"LISTEN", "SYN_SENT", "SYN_RECEIVED", "ESTABLISHED"} { + nopen += bytes.Count(lsof, []byte(state)) + } + for _, state := range []string{"CLOSED", "CLOSE_WAIT", "LAST_ACK", "FIN_WAIT_1", "FIN_WAIT_2", "CLOSING", "TIME_WAIT"} { + nclose += bytes.Count(lsof, []byte(state)) + } + return ntcp, nopen, nclose, nil +} + +func TestDialMultiFDLeak(t *testing.T) { + if !supportsIPv4 || !supportsIPv6 { + t.Skip("neither ipv4 nor ipv6 is supported") + } + + halfDeadServer := func(dss *dualStackServer, ln Listener) { + for { + if c, err := ln.Accept(); err != nil { + return + } else { + // It just keeps established + // connections like a half-dead server + // does. + dss.putConn(c) + } + } + } + dss, err := newDualStackServer([]streamListener{ + {net: "tcp4", addr: "127.0.0.1"}, + {net: "tcp6", addr: "[::1]"}, + }) + if err != nil { + t.Fatalf("newDualStackServer failed: %v", err) + } + defer dss.teardown() + if err := dss.buildup(halfDeadServer); err != nil { + t.Fatalf("dualStackServer.buildup failed: %v", err) + } + + _, before, _, err := numTCP() + if err != nil { + t.Skipf("skipping test; error finding or running lsof: %v", err) + } + + var wg sync.WaitGroup + portnum, _, _ := dtoi(dss.port, 0) + ras := addrList{ + // Losers that will fail to connect, see RFC 6890. + &TCPAddr{IP: IPv4(198, 18, 0, 254), Port: portnum}, + &TCPAddr{IP: ParseIP("2001:2::254"), Port: portnum}, + + // Winner candidates of this race. + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum}, + &TCPAddr{IP: IPv6loopback, Port: portnum}, + + // Losers that will have established connections. + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: portnum}, + &TCPAddr{IP: IPv6loopback, Port: portnum}, + } + const T1 = 10 * time.Millisecond + const T2 = 2 * T1 + const N = 10 + for i := 0; i < N; i++ { + wg.Add(1) + go func() { + defer wg.Done() + if c, err := dialMulti("tcp", "fast failover test", nil, ras, time.Now().Add(T1)); err == nil { + c.Close() + } + }() + } + wg.Wait() + time.Sleep(T2) + + ntcp, after, nclose, err := numTCP() + if err != nil { + t.Skipf("skipping test; error finding or running lsof: %v", err) + } + t.Logf("tcp sessions: %v, open sessions: %v, closing sessions: %v", ntcp, after, nclose) + + if after != before { + t.Fatalf("got %v open sessions; expected %v", after, before) + } +} + func numFD() int { if runtime.GOOS == "linux" { f, err := os.Open("/proc/self/fd") @@ -331,17 +425,22 @@ func numFD() int { panic("numFDs not implemented on " + runtime.GOOS) } -var testPoller = flag.Bool("poller", false, "platform supports runtime-integrated poller") - // Assert that a failed Dial attempt does not leak // runtime.PollDesc structures func TestDialFailPDLeak(t *testing.T) { - if !*testPoller { - t.Skip("test disabled; use -poller to enable") + 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) } - const loops = 10 - const count = 20000 + 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 { @@ -354,19 +453,26 @@ func TestDialFailPDLeak(t *testing.T) { 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++ { - conn, err := d.Dial("tcp", "127.0.0.1:1") - if err == nil { - t.Error("dial should not succeed") - conn.Close() - t.FailNow() - } + 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 > 3 { + if failcount > maxprocs+2 { t.Error("detected possible memory leak in runtime") t.FailNow() } @@ -381,7 +487,6 @@ func TestDialer(t *testing.T) { defer ln.Close() ch := make(chan error, 1) go func() { - var err error c, err := ln.Accept() if err != nil { ch <- fmt.Errorf("Accept failed: %v", err) @@ -407,3 +512,46 @@ func TestDialer(t *testing.T) { t.Error(err) } } + +func TestDialDualStackLocalhost(t *testing.T) { + if ips, err := LookupIP("localhost"); err != nil { + t.Fatalf("LookupIP failed: %v", err) + } else if len(ips) < 2 || !supportsIPv4 || !supportsIPv6 { + t.Skip("localhost doesn't have a pair of different address family IP addresses") + } + + touchAndByeServer := func(dss *dualStackServer, ln Listener) { + for { + if c, err := ln.Accept(); err != nil { + return + } else { + c.Close() + } + } + } + dss, err := newDualStackServer([]streamListener{ + {net: "tcp4", addr: "127.0.0.1"}, + {net: "tcp6", addr: "[::1]"}, + }) + if err != nil { + t.Fatalf("newDualStackServer failed: %v", err) + } + defer dss.teardown() + if err := dss.buildup(touchAndByeServer); err != nil { + t.Fatalf("dualStackServer.buildup failed: %v", err) + } + + d := &Dialer{DualStack: true} + for _ = range dss.lns { + if c, err := d.Dial("tcp", "localhost:"+dss.port); err != nil { + t.Errorf("Dial failed: %v", err) + } else { + if addr := c.LocalAddr().(*TCPAddr); addr.IP.To4() != nil { + dss.teardownNetwork("tcp4") + } else if addr.IP.To16() != nil && addr.IP.To4() == nil { + dss.teardownNetwork("tcp6") + } + c.Close() + } + } +} diff --git a/libgo/go/net/dialgoogle_test.go b/libgo/go/net/dialgoogle_test.go index 73a94f5..b4ebad0 100644 --- a/libgo/go/net/dialgoogle_test.go +++ b/libgo/go/net/dialgoogle_test.go @@ -16,6 +16,59 @@ import ( // If an IPv6 tunnel is running, we can try dialing a real IPv6 address. var testIPv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present") +func TestResolveGoogle(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + + for _, network := range []string{"tcp", "tcp4", "tcp6"} { + addr, err := ResolveTCPAddr(network, "www.google.com:http") + if err != nil { + if (network == "tcp" || network == "tcp4") && !supportsIPv4 { + t.Logf("ipv4 is not supported: %v", err) + } else if network == "tcp6" && !supportsIPv6 { + t.Logf("ipv6 is not supported: %v", err) + } else { + t.Errorf("ResolveTCPAddr failed: %v", err) + } + continue + } + if (network == "tcp" || network == "tcp4") && addr.IP.To4() == nil { + t.Errorf("got %v; expected an IPv4 address on %v", addr, network) + } else if network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil) { + t.Errorf("got %v; expected an IPv6 address on %v", addr, network) + } + } +} + +func TestDialGoogle(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + + d := &Dialer{DualStack: true} + for _, network := range []string{"tcp", "tcp4", "tcp6"} { + if network == "tcp" && !supportsIPv4 && !supportsIPv6 { + t.Logf("skipping test; both ipv4 and ipv6 are not supported") + continue + } else if network == "tcp4" && !supportsIPv4 { + t.Logf("skipping test; ipv4 is not supported") + continue + } else if network == "tcp6" && !supportsIPv6 { + t.Logf("skipping test; ipv6 is not supported") + continue + } else if network == "tcp6" && !*testIPv6 { + t.Logf("test disabled; use -ipv6 to enable") + continue + } + if c, err := d.Dial(network, "www.google.com:http"); err != nil { + t.Errorf("Dial failed: %v", err) + } else { + c.Close() + } + } +} + // fd is already connected to the destination, port 80. // Run an HTTP request to fetch the appropriate page. func fetchGoogle(t *testing.T, fd Conn, network, addr string) { @@ -54,6 +107,30 @@ var googleaddrsipv4 = []string{ "[0:0:0:0:0:ffff::%d.%d.%d.%d]:80", } +func TestDNSThreadLimit(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + + const N = 10000 + c := make(chan int, N) + for i := 0; i < N; i++ { + go func(i int) { + LookupIP(fmt.Sprintf("%d.net-test.golang.org", i)) + c <- 1 + }(i) + } + // Don't bother waiting for the stragglers; stop at 0.9 N. + for i := 0; i < N*9/10; i++ { + if i%100 == 0 { + //println("TestDNSThreadLimit:", i) + } + <-c + } + + // If we're still here, it worked. +} + func TestDialGoogleIPv4(t *testing.T) { if testing.Short() || !*testExternal { t.Skip("skipping test to avoid external network") diff --git a/libgo/go/net/dnsclient.go b/libgo/go/net/dnsclient.go index 76b1926..01db437 100644 --- a/libgo/go/net/dnsclient.go +++ b/libgo/go/net/dnsclient.go @@ -122,12 +122,9 @@ func isDomainName(s string) bool { if len(s) > 255 { return false } - if s[len(s)-1] != '.' { // simplify checking loop: make name end in dot - s += "." - } last := byte('.') - ok := false // ok once we've seen a letter + ok := false // Ok once we've seen a letter. partlen := 0 for i := 0; i < len(s); i++ { c := s[i] @@ -141,13 +138,13 @@ func isDomainName(s string) bool { // fine partlen++ case c == '-': - // byte before dash cannot be dot + // Byte before dash cannot be dot. if last == '.' { return false } partlen++ case c == '.': - // byte before dot cannot be dot, dash + // Byte before dot cannot be dot, dash. if last == '.' || last == '-' { return false } @@ -158,6 +155,9 @@ func isDomainName(s string) bool { } last = c } + if last == '-' || partlen > 63 { + return false + } return ok } diff --git a/libgo/go/net/dnsclient_unix.go b/libgo/go/net/dnsclient_unix.go index 9e21bb4..16cf420 100644 --- a/libgo/go/net/dnsclient_unix.go +++ b/libgo/go/net/dnsclient_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // DNS client: see RFC 1035. // Has to be linked into package net for Dial. @@ -17,6 +17,7 @@ package net import ( + "io" "math/rand" "sync" "time" @@ -25,6 +26,7 @@ import ( // Send a request on the connection and hope for a reply. // Up to cfg.attempts attempts. func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error) { + _, useTCP := c.(*TCPConn) if len(name) >= 256 { return nil, &DNSError{Err: "name too long", Name: name} } @@ -38,7 +40,10 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error if !ok { return nil, &DNSError{Err: "internal error - cannot pack message", Name: name} } - + if useTCP { + mlen := uint16(len(msg)) + msg = append([]byte{byte(mlen >> 8), byte(mlen)}, msg...) + } for attempt := 0; attempt < cfg.attempts; attempt++ { n, err := c.Write(msg) if err != nil { @@ -46,20 +51,33 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error } if cfg.timeout == 0 { - c.SetReadDeadline(time.Time{}) + c.SetReadDeadline(noDeadline) } else { c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second)) } - - buf := make([]byte, 2000) // More than enough. - n, err = c.Read(buf) + buf := make([]byte, 2000) + if useTCP { + n, err = io.ReadFull(c, buf[:2]) + if err != nil { + if e, ok := err.(Error); ok && e.Timeout() { + continue + } + } + mlen := int(buf[0])<<8 | int(buf[1]) + if mlen > len(buf) { + buf = make([]byte, mlen) + } + n, err = io.ReadFull(c, buf[:mlen]) + } else { + n, err = c.Read(buf) + } if err != nil { if e, ok := err.(Error); ok && e.Timeout() { continue } return nil, err } - buf = buf[0:n] + buf = buf[:n] in := new(dnsMsg) if !in.Unpack(buf) || in.id != out.id { continue @@ -98,6 +116,19 @@ func tryOneName(cfg *dnsConfig, name string, qtype uint16) (cname string, addrs err = merr continue } + if msg.truncated { // see RFC 5966 + c, cerr = Dial("tcp", server) + if cerr != nil { + err = cerr + continue + } + msg, merr = exchange(cfg, c, name, qtype) + c.Close() + if merr != nil { + err = merr + continue + } + } cname, addrs, err = answer(name, server, msg, qtype) if err == nil || err.(*DNSError).Err == noSuchHost { break @@ -180,6 +211,12 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) if err == nil { return } + if e, ok := err.(*DNSError); ok { + // Show original name passed to lookup, not suffixed one. + // In general we might have tried many suffixes; showing + // just one is misleading. See also golang.org/issue/6324. + e.Name = name + } return } diff --git a/libgo/go/net/dnsclient_unix_test.go b/libgo/go/net/dnsclient_unix_test.go new file mode 100644 index 0000000..47dcb56 --- /dev/null +++ b/libgo/go/net/dnsclient_unix_test.go @@ -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. + +// +build darwin dragonfly freebsd linux netbsd openbsd + +package net + +import ( + "testing" +) + +func TestTCPLookup(t *testing.T) { + if testing.Short() || !*testExternal { + t.Skip("skipping test to avoid external network") + } + c, err := Dial("tcp", "8.8.8.8:53") + if err != nil { + t.Fatalf("Dial failed: %v", err) + } + defer c.Close() + cfg := &dnsConfig{timeout: 10, attempts: 3} + _, err = exchange(cfg, c, "com.", dnsTypeALL) + if err != nil { + t.Fatalf("exchange failed: %v", err) + } +} diff --git a/libgo/go/net/dnsconfig_unix.go b/libgo/go/net/dnsconfig_unix.go index bb46cc9..2f0f6c0 100644 --- a/libgo/go/net/dnsconfig_unix.go +++ b/libgo/go/net/dnsconfig_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // Read system DNS config from /etc/resolv.conf diff --git a/libgo/go/net/dnsname_test.go b/libgo/go/net/dnsname_test.go index 70df693..57dd25f 100644 --- a/libgo/go/net/dnsname_test.go +++ b/libgo/go/net/dnsname_test.go @@ -5,6 +5,7 @@ package net import ( + "strings" "testing" ) @@ -16,7 +17,6 @@ type testCase struct { var tests = []testCase{ // RFC2181, section 11. {"_xmpp-server._tcp.google.com", true}, - {"_xmpp-server._tcp.google.com", true}, {"foo.com", true}, {"1foo.com", true}, {"26.0.0.73.com", true}, @@ -24,6 +24,10 @@ var tests = []testCase{ {"fo1o.com", true}, {"foo1.com", true}, {"a.b..com", false}, + {"a.b-.com", false}, + {"a.b.com-", false}, + {"a.b..", false}, + {"b.com.", true}, } func getTestCases(ch chan<- testCase) { @@ -63,3 +67,17 @@ func TestDNSNames(t *testing.T) { } } } + +func BenchmarkDNSNames(b *testing.B) { + benchmarks := append(tests, []testCase{ + {strings.Repeat("a", 63), true}, + {strings.Repeat("a", 64), false}, + }...) + for n := 0; n < b.N; n++ { + for _, tc := range benchmarks { + if isDomainName(tc.name) != tc.result { + b.Errorf("isDomainName(%q) = %v; want %v", tc.name, !tc.result, tc.result) + } + } + } +} diff --git a/libgo/go/net/fd_bsd.go b/libgo/go/net/fd_bsd.go deleted file mode 100644 index 8bb1ae5..0000000 --- a/libgo/go/net/fd_bsd.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build freebsd netbsd openbsd - -// Waiting for FDs via kqueue/kevent. - -package net - -import ( - "os" - "syscall" -) - -type pollster struct { - kq int - eventbuf [10]syscall.Kevent_t - events []syscall.Kevent_t - - // An event buffer for AddFD/DelFD. - // Must hold pollServer lock. - kbuf [1]syscall.Kevent_t -} - -func newpollster() (p *pollster, err error) { - p = new(pollster) - if p.kq, err = syscall.Kqueue(); err != nil { - return nil, os.NewSyscallError("kqueue", err) - } - syscall.CloseOnExec(p.kq) - p.events = p.eventbuf[0:0] - return p, nil -} - -// First return value is whether the pollServer should be woken up. -// This version always returns false. -func (p *pollster) AddFD(fd int, mode int, repeat bool) (bool, error) { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_ADD - add event to kqueue list - // EV_ONESHOT - delete the event the first time it triggers - flags := syscall.EV_ADD - if !repeat { - flags |= syscall.EV_ONESHOT - } - syscall.SetKevent(ev, fd, kmode, flags) - - n, err := syscall.Kevent(p.kq, p.kbuf[:], nil, nil) - if err != nil { - return false, os.NewSyscallError("kevent", err) - } - if n != 1 || (ev.Flags&syscall.EV_ERROR) == 0 || int(ev.Ident) != fd || int(ev.Filter) != kmode { - return false, os.NewSyscallError("kqueue phase error", err) - } - if ev.Data != 0 { - return false, syscall.Errno(int(ev.Data)) - } - return false, nil -} - -// Return value is whether the pollServer should be woken up. -// This version always returns false. -func (p *pollster) DelFD(fd int, mode int) bool { - // pollServer is locked. - - var kmode int - if mode == 'r' { - kmode = syscall.EVFILT_READ - } else { - kmode = syscall.EVFILT_WRITE - } - ev := &p.kbuf[0] - // EV_DELETE - delete event from kqueue list - syscall.SetKevent(ev, fd, kmode, syscall.EV_DELETE) - syscall.Kevent(p.kq, p.kbuf[:], nil, nil) - return false -} - -func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err error) { - var t *syscall.Timespec - for len(p.events) == 0 { - if nsec > 0 { - if t == nil { - t = new(syscall.Timespec) - } - *t = syscall.NsecToTimespec(nsec) - } - - s.Unlock() - n, err := syscall.Kevent(p.kq, nil, p.eventbuf[:], t) - s.Lock() - - if err != nil { - if err == syscall.EINTR { - continue - } - return -1, 0, os.NewSyscallError("kevent", err) - } - if n == 0 { - return -1, 0, nil - } - p.events = p.eventbuf[:n] - } - ev := &p.events[0] - p.events = p.events[1:] - fd = int(ev.Ident) - if ev.Filter == syscall.EVFILT_READ { - mode = 'r' - } else { - mode = 'w' - } - return fd, mode, nil -} - -func (p *pollster) Close() error { return os.NewSyscallError("close", syscall.Close(p.kq)) } diff --git a/libgo/go/net/fd_mutex.go b/libgo/go/net/fd_mutex.go new file mode 100644 index 0000000..6d5509d --- /dev/null +++ b/libgo/go/net/fd_mutex.go @@ -0,0 +1,184 @@ +// 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 "sync/atomic" + +// fdMutex is a specialized synchronization primitive +// that manages lifetime of an fd and serializes access +// to Read and Write methods on netFD. +type fdMutex struct { + state uint64 + rsema uint32 + wsema uint32 +} + +// fdMutex.state is organized as follows: +// 1 bit - whether netFD is closed, if set all subsequent lock operations will fail. +// 1 bit - lock for read operations. +// 1 bit - lock for write operations. +// 20 bits - total number of references (read+write+misc). +// 20 bits - number of outstanding read waiters. +// 20 bits - number of outstanding write waiters. +const ( + mutexClosed = 1 << 0 + mutexRLock = 1 << 1 + mutexWLock = 1 << 2 + mutexRef = 1 << 3 + mutexRefMask = (1<<20 - 1) << 3 + mutexRWait = 1 << 23 + mutexRMask = (1<<20 - 1) << 23 + mutexWWait = 1 << 43 + mutexWMask = (1<<20 - 1) << 43 +) + +// Read operations must do RWLock(true)/RWUnlock(true). +// Write operations must do RWLock(false)/RWUnlock(false). +// Misc operations must do Incref/Decref. Misc operations include functions like +// setsockopt and setDeadline. They need to use Incref/Decref to ensure that +// they operate on the correct fd in presence of a concurrent Close call +// (otherwise fd can be closed under their feet). +// Close operation must do IncrefAndClose/Decref. + +// RWLock/Incref return whether fd is open. +// RWUnlock/Decref return whether fd is closed and there are no remaining references. + +func (mu *fdMutex) Incref() bool { + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexClosed != 0 { + return false + } + new := old + mutexRef + if new&mutexRefMask == 0 { + panic("net: inconsistent fdMutex") + } + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + return true + } + } +} + +func (mu *fdMutex) IncrefAndClose() bool { + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexClosed != 0 { + return false + } + // Mark as closed and acquire a reference. + new := (old | mutexClosed) + mutexRef + if new&mutexRefMask == 0 { + panic("net: inconsistent fdMutex") + } + // Remove all read and write waiters. + new &^= mutexRMask | mutexWMask + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + // Wake all read and write waiters, + // they will observe closed flag after wakeup. + for old&mutexRMask != 0 { + old -= mutexRWait + runtime_Semrelease(&mu.rsema) + } + for old&mutexWMask != 0 { + old -= mutexWWait + runtime_Semrelease(&mu.wsema) + } + return true + } + } +} + +func (mu *fdMutex) Decref() bool { + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexRefMask == 0 { + panic("net: inconsistent fdMutex") + } + new := old - mutexRef + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + return new&(mutexClosed|mutexRefMask) == mutexClosed + } + } +} + +func (mu *fdMutex) RWLock(read bool) bool { + var mutexBit, mutexWait, mutexMask uint64 + var mutexSema *uint32 + if read { + mutexBit = mutexRLock + mutexWait = mutexRWait + mutexMask = mutexRMask + mutexSema = &mu.rsema + } else { + mutexBit = mutexWLock + mutexWait = mutexWWait + mutexMask = mutexWMask + mutexSema = &mu.wsema + } + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexClosed != 0 { + return false + } + var new uint64 + if old&mutexBit == 0 { + // Lock is free, acquire it. + new = (old | mutexBit) + mutexRef + if new&mutexRefMask == 0 { + panic("net: inconsistent fdMutex") + } + } else { + // Wait for lock. + new = old + mutexWait + if new&mutexMask == 0 { + panic("net: inconsistent fdMutex") + } + } + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + if old&mutexBit == 0 { + return true + } + runtime_Semacquire(mutexSema) + // The signaller has subtracted mutexWait. + } + } +} + +func (mu *fdMutex) RWUnlock(read bool) bool { + var mutexBit, mutexWait, mutexMask uint64 + var mutexSema *uint32 + if read { + mutexBit = mutexRLock + mutexWait = mutexRWait + mutexMask = mutexRMask + mutexSema = &mu.rsema + } else { + mutexBit = mutexWLock + mutexWait = mutexWWait + mutexMask = mutexWMask + mutexSema = &mu.wsema + } + for { + old := atomic.LoadUint64(&mu.state) + if old&mutexBit == 0 || old&mutexRefMask == 0 { + panic("net: inconsistent fdMutex") + } + // Drop lock, drop reference and wake read waiter if present. + new := (old &^ mutexBit) - mutexRef + if old&mutexMask != 0 { + new -= mutexWait + } + if atomic.CompareAndSwapUint64(&mu.state, old, new) { + if old&mutexMask != 0 { + runtime_Semrelease(mutexSema) + } + return new&(mutexClosed|mutexRefMask) == mutexClosed + } + } +} + +// Implemented in runtime package. +func runtime_Semacquire(sema *uint32) +func runtime_Semrelease(sema *uint32) diff --git a/libgo/go/net/fd_mutex_test.go b/libgo/go/net/fd_mutex_test.go new file mode 100644 index 0000000..8383084 --- /dev/null +++ b/libgo/go/net/fd_mutex_test.go @@ -0,0 +1,186 @@ +// 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 ( + "math/rand" + "runtime" + "testing" + "time" +) + +func TestMutexLock(t *testing.T) { + var mu fdMutex + + if !mu.Incref() { + t.Fatal("broken") + } + if mu.Decref() { + t.Fatal("broken") + } + + if !mu.RWLock(true) { + t.Fatal("broken") + } + if mu.RWUnlock(true) { + t.Fatal("broken") + } + + if !mu.RWLock(false) { + t.Fatal("broken") + } + if mu.RWUnlock(false) { + t.Fatal("broken") + } +} + +func TestMutexClose(t *testing.T) { + var mu fdMutex + if !mu.IncrefAndClose() { + t.Fatal("broken") + } + + if mu.Incref() { + t.Fatal("broken") + } + if mu.RWLock(true) { + t.Fatal("broken") + } + if mu.RWLock(false) { + t.Fatal("broken") + } + if mu.IncrefAndClose() { + t.Fatal("broken") + } +} + +func TestMutexCloseUnblock(t *testing.T) { + c := make(chan bool) + var mu fdMutex + mu.RWLock(true) + for i := 0; i < 4; i++ { + go func() { + if mu.RWLock(true) { + t.Fatal("broken") + } + c <- true + }() + } + // Concurrent goroutines must not be able to read lock the mutex. + time.Sleep(time.Millisecond) + select { + case <-c: + t.Fatal("broken") + default: + } + mu.IncrefAndClose() // Must unblock the readers. + for i := 0; i < 4; i++ { + select { + case <-c: + case <-time.After(10 * time.Second): + t.Fatal("broken") + } + } + if mu.Decref() { + t.Fatal("broken") + } + if !mu.RWUnlock(true) { + t.Fatal("broken") + } +} + +func TestMutexPanic(t *testing.T) { + ensurePanics := func(f func()) { + defer func() { + if recover() == nil { + t.Fatal("does not panic") + } + }() + f() + } + + var mu fdMutex + ensurePanics(func() { mu.Decref() }) + ensurePanics(func() { mu.RWUnlock(true) }) + ensurePanics(func() { mu.RWUnlock(false) }) + + ensurePanics(func() { mu.Incref(); mu.Decref(); mu.Decref() }) + ensurePanics(func() { mu.RWLock(true); mu.RWUnlock(true); mu.RWUnlock(true) }) + ensurePanics(func() { mu.RWLock(false); mu.RWUnlock(false); mu.RWUnlock(false) }) + + // ensure that it's still not broken + mu.Incref() + mu.Decref() + mu.RWLock(true) + mu.RWUnlock(true) + mu.RWLock(false) + mu.RWUnlock(false) +} + +func TestMutexStress(t *testing.T) { + P := 8 + N := int(1e6) + if testing.Short() { + P = 4 + N = 1e4 + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) + done := make(chan bool) + var mu fdMutex + var readState [2]uint64 + var writeState [2]uint64 + for p := 0; p < P; p++ { + go func() { + r := rand.New(rand.NewSource(rand.Int63())) + for i := 0; i < N; i++ { + switch r.Intn(3) { + case 0: + if !mu.Incref() { + t.Fatal("broken") + } + if mu.Decref() { + t.Fatal("broken") + } + case 1: + if !mu.RWLock(true) { + t.Fatal("broken") + } + // Ensure that it provides mutual exclusion for readers. + if readState[0] != readState[1] { + t.Fatal("broken") + } + readState[0]++ + readState[1]++ + if mu.RWUnlock(true) { + t.Fatal("broken") + } + case 2: + if !mu.RWLock(false) { + t.Fatal("broken") + } + // Ensure that it provides mutual exclusion for writers. + if writeState[0] != writeState[1] { + t.Fatal("broken") + } + writeState[0]++ + writeState[1]++ + if mu.RWUnlock(false) { + t.Fatal("broken") + } + } + } + done <- true + }() + } + for p := 0; p < P; p++ { + <-done + } + if !mu.IncrefAndClose() { + t.Fatal("broken") + } + if !mu.Decref() { + t.Fatal("broken") + } +} diff --git a/libgo/go/net/fd_plan9.go b/libgo/go/net/fd_plan9.go index e9527a3..acc8294 100644 --- a/libgo/go/net/fd_plan9.go +++ b/libgo/go/net/fd_plan9.go @@ -18,15 +18,13 @@ type netFD struct { laddr, raddr Addr } -var canCancelIO = true // used for testing current package - func sysInit() { } -func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) { +func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) { // On plan9, use the relatively inefficient // goroutine-racing implementation. - return resolveAndDialChannel(net, addr, localAddr, deadline) + return dialChannel(net, ra, dialer, deadline) } func newFD(proto, name string, ctl, data *os.File, laddr, raddr Addr) *netFD { @@ -108,15 +106,15 @@ func (fd *netFD) file(f *os.File, s string) (*os.File, error) { return os.NewFile(uintptr(dfd), s), nil } -func setDeadline(fd *netFD, t time.Time) error { +func (fd *netFD) setDeadline(t time.Time) error { return syscall.EPLAN9 } -func setReadDeadline(fd *netFD, t time.Time) error { +func (fd *netFD) setReadDeadline(t time.Time) error { return syscall.EPLAN9 } -func setWriteDeadline(fd *netFD, t time.Time) error { +func (fd *netFD) setWriteDeadline(t time.Time) error { return syscall.EPLAN9 } @@ -127,3 +125,7 @@ func setReadBuffer(fd *netFD, bytes int) error { func setWriteBuffer(fd *netFD, bytes int) error { return syscall.EPLAN9 } + +func skipRawSocketTests() (skip bool, skipmsg string, err error) { + return true, "skipping test on plan9", nil +} diff --git a/libgo/go/net/fd_poll_runtime.go b/libgo/go/net/fd_poll_runtime.go index e3b4f7e..e2b2768 100644 --- a/libgo/go/net/fd_poll_runtime.go +++ b/libgo/go/net/fd_poll_runtime.go @@ -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 linux +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -13,27 +13,23 @@ import ( ) func runtime_pollServerInit() -func runtime_pollOpen(fd int) (uintptr, int) +func runtime_pollOpen(fd uintptr) (uintptr, int) func runtime_pollClose(ctx uintptr) func runtime_pollWait(ctx uintptr, mode int) int +func runtime_pollWaitCanceled(ctx uintptr, mode int) int func runtime_pollReset(ctx uintptr, mode int) int func runtime_pollSetDeadline(ctx uintptr, d int64, mode int) func runtime_pollUnblock(ctx uintptr) -var canCancelIO = true // used for testing current package - type pollDesc struct { runtimeCtx uintptr } var serverInit sync.Once -func sysInit() { -} - func (pd *pollDesc) Init(fd *netFD) error { serverInit.Do(runtime_pollServerInit) - ctx, errno := runtime_pollOpen(fd.sysfd) + ctx, errno := runtime_pollOpen(uintptr(fd.sysfd)) if errno != 0 { return syscall.Errno(errno) } @@ -42,7 +38,11 @@ func (pd *pollDesc) Init(fd *netFD) error { } func (pd *pollDesc) Close() { + if pd.runtimeCtx == 0 { + return + } runtime_pollClose(pd.runtimeCtx) + pd.runtimeCtx = 0 } func (pd *pollDesc) Lock() { @@ -57,28 +57,49 @@ func (pd *pollDesc) Wakeup() { // Evict evicts fd from the pending list, unblocking any I/O running on fd. // Return value is whether the pollServer should be woken up. func (pd *pollDesc) Evict() bool { + if pd.runtimeCtx == 0 { + return false + } runtime_pollUnblock(pd.runtimeCtx) return false } -func (pd *pollDesc) PrepareRead() error { - res := runtime_pollReset(pd.runtimeCtx, 'r') +func (pd *pollDesc) Prepare(mode int) error { + res := runtime_pollReset(pd.runtimeCtx, mode) return convertErr(res) } +func (pd *pollDesc) PrepareRead() error { + return pd.Prepare('r') +} + func (pd *pollDesc) PrepareWrite() error { - res := runtime_pollReset(pd.runtimeCtx, 'w') + return pd.Prepare('w') +} + +func (pd *pollDesc) Wait(mode int) error { + res := runtime_pollWait(pd.runtimeCtx, mode) return convertErr(res) } func (pd *pollDesc) WaitRead() error { - res := runtime_pollWait(pd.runtimeCtx, 'r') - return convertErr(res) + return pd.Wait('r') } func (pd *pollDesc) WaitWrite() error { - res := runtime_pollWait(pd.runtimeCtx, 'w') - return convertErr(res) + return pd.Wait('w') +} + +func (pd *pollDesc) WaitCanceled(mode int) { + runtime_pollWaitCanceled(pd.runtimeCtx, mode) +} + +func (pd *pollDesc) WaitCanceledRead() { + pd.WaitCanceled('r') +} + +func (pd *pollDesc) WaitCanceledWrite() { + pd.WaitCanceled('w') } func convertErr(res int) error { @@ -90,19 +111,20 @@ func convertErr(res int) error { case 2: return errTimeout } + println("unreachable: ", res) panic("unreachable") } -func setReadDeadline(fd *netFD, t time.Time) error { - return setDeadlineImpl(fd, t, 'r') +func (fd *netFD) setDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'r'+'w') } -func setWriteDeadline(fd *netFD, t time.Time) error { - return setDeadlineImpl(fd, t, 'w') +func (fd *netFD) setReadDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'r') } -func setDeadline(fd *netFD, t time.Time) error { - return setDeadlineImpl(fd, t, 'r'+'w') +func (fd *netFD) setWriteDeadline(t time.Time) error { + return setDeadlineImpl(fd, t, 'w') } func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { @@ -110,7 +132,7 @@ func setDeadlineImpl(fd *netFD, t time.Time, mode int) error { if t.IsZero() { d = 0 } - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } runtime_pollSetDeadline(fd.pd.runtimeCtx, d, mode) diff --git a/libgo/go/net/fd_poll_unix.go b/libgo/go/net/fd_poll_unix.go deleted file mode 100644 index 307e577..0000000 --- a/libgo/go/net/fd_poll_unix.go +++ /dev/null @@ -1,360 +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 netbsd openbsd - -package net - -import ( - "os" - "runtime" - "sync" - "syscall" - "time" -) - -// A pollServer helps FDs determine when to retry a non-blocking -// read or write after they get EAGAIN. When an FD needs to wait, -// call s.WaitRead() or s.WaitWrite() to pass the request to the poll server. -// When the pollServer finds that i/o on FD should be possible -// again, it will send on fd.cr/fd.cw to wake any waiting goroutines. -// -// To avoid races in closing, all fd operations are locked and -// refcounted. when netFD.Close() is called, it calls syscall.Shutdown -// and sets a closing flag. Only when the last reference is removed -// will the fd be closed. - -type pollServer struct { - pr, pw *os.File - poll *pollster // low-level OS hooks - sync.Mutex // controls pending and deadline - pending map[int]*pollDesc - deadline int64 // next deadline (nsec since 1970) -} - -// A pollDesc contains netFD state related to pollServer. -type pollDesc struct { - // immutable after Init() - pollServer *pollServer - sysfd int - cr, cw chan error - - // mutable, protected by pollServer mutex - closing bool - ncr, ncw int - - // mutable, safe for concurrent access - rdeadline, wdeadline deadline -} - -func newPollServer() (s *pollServer, err error) { - s = new(pollServer) - if s.pr, s.pw, err = os.Pipe(); err != nil { - return nil, err - } - if err = syscall.SetNonblock(int(s.pr.Fd()), true); err != nil { - goto Errno - } - if err = syscall.SetNonblock(int(s.pw.Fd()), true); err != nil { - goto Errno - } - if s.poll, err = newpollster(); err != nil { - goto Error - } - if _, err = s.poll.AddFD(int(s.pr.Fd()), 'r', true); err != nil { - s.poll.Close() - goto Error - } - s.pending = make(map[int]*pollDesc) - go s.Run() - return s, nil - -Errno: - err = &os.PathError{ - Op: "setnonblock", - Path: s.pr.Name(), - Err: err, - } -Error: - s.pr.Close() - s.pw.Close() - return nil, err -} - -func (s *pollServer) AddFD(pd *pollDesc, mode int) error { - s.Lock() - intfd := pd.sysfd - if intfd < 0 || pd.closing { - // fd closed underfoot - s.Unlock() - return errClosing - } - - var t int64 - key := intfd << 1 - if mode == 'r' { - pd.ncr++ - t = pd.rdeadline.value() - } else { - pd.ncw++ - key++ - t = pd.wdeadline.value() - } - s.pending[key] = pd - doWakeup := false - if t > 0 && (s.deadline == 0 || t < s.deadline) { - s.deadline = t - doWakeup = true - } - - wake, err := s.poll.AddFD(intfd, mode, false) - s.Unlock() - if err != nil { - return err - } - if wake || doWakeup { - s.Wakeup() - } - return nil -} - -// Evict evicts pd from the pending list, unblocking -// any I/O running on pd. The caller must have locked -// pollserver. -// Return value is whether the pollServer should be woken up. -func (s *pollServer) Evict(pd *pollDesc) bool { - pd.closing = true - doWakeup := false - if s.pending[pd.sysfd<<1] == pd { - s.WakeFD(pd, 'r', errClosing) - if s.poll.DelFD(pd.sysfd, 'r') { - doWakeup = true - } - delete(s.pending, pd.sysfd<<1) - } - if s.pending[pd.sysfd<<1|1] == pd { - s.WakeFD(pd, 'w', errClosing) - if s.poll.DelFD(pd.sysfd, 'w') { - doWakeup = true - } - delete(s.pending, pd.sysfd<<1|1) - } - return doWakeup -} - -var wakeupbuf [1]byte - -func (s *pollServer) Wakeup() { s.pw.Write(wakeupbuf[0:]) } - -func (s *pollServer) LookupFD(fd int, mode int) *pollDesc { - key := fd << 1 - if mode == 'w' { - key++ - } - netfd, ok := s.pending[key] - if !ok { - return nil - } - delete(s.pending, key) - return netfd -} - -func (s *pollServer) WakeFD(pd *pollDesc, mode int, err error) { - if mode == 'r' { - for pd.ncr > 0 { - pd.ncr-- - pd.cr <- err - } - } else { - for pd.ncw > 0 { - pd.ncw-- - pd.cw <- err - } - } -} - -func (s *pollServer) CheckDeadlines() { - now := time.Now().UnixNano() - // TODO(rsc): This will need to be handled more efficiently, - // probably with a heap indexed by wakeup time. - - var nextDeadline int64 - for key, pd := range s.pending { - var t int64 - var mode int - if key&1 == 0 { - mode = 'r' - } else { - mode = 'w' - } - if mode == 'r' { - t = pd.rdeadline.value() - } else { - t = pd.wdeadline.value() - } - if t > 0 { - if t <= now { - delete(s.pending, key) - s.poll.DelFD(pd.sysfd, mode) - s.WakeFD(pd, mode, errTimeout) - } else if nextDeadline == 0 || t < nextDeadline { - nextDeadline = t - } - } - } - s.deadline = nextDeadline -} - -func (s *pollServer) Run() { - var scratch [100]byte - s.Lock() - defer s.Unlock() - for { - var timeout int64 // nsec to wait for or 0 for none - if s.deadline > 0 { - timeout = s.deadline - time.Now().UnixNano() - if timeout <= 0 { - s.CheckDeadlines() - continue - } - } - fd, mode, err := s.poll.WaitFD(s, timeout) - if err != nil { - print("pollServer WaitFD: ", err.Error(), "\n") - return - } - if fd < 0 { - // Timeout happened. - s.CheckDeadlines() - continue - } - if fd == int(s.pr.Fd()) { - // Drain our wakeup pipe (we could loop here, - // but it's unlikely that there are more than - // len(scratch) wakeup calls). - s.pr.Read(scratch[0:]) - s.CheckDeadlines() - } else { - pd := s.LookupFD(fd, mode) - if pd == nil { - // This can happen because the WaitFD runs without - // holding s's lock, so there might be a pending wakeup - // for an fd that has been evicted. No harm done. - continue - } - s.WakeFD(pd, mode, nil) - } - } -} - -func (pd *pollDesc) Close() { -} - -func (pd *pollDesc) Lock() { - pd.pollServer.Lock() -} - -func (pd *pollDesc) Unlock() { - pd.pollServer.Unlock() -} - -func (pd *pollDesc) Wakeup() { - pd.pollServer.Wakeup() -} - -func (pd *pollDesc) PrepareRead() error { - if pd.rdeadline.expired() { - return errTimeout - } - return nil -} - -func (pd *pollDesc) PrepareWrite() error { - if pd.wdeadline.expired() { - return errTimeout - } - return nil -} - -func (pd *pollDesc) WaitRead() error { - err := pd.pollServer.AddFD(pd, 'r') - if err == nil { - err = <-pd.cr - } - return err -} - -func (pd *pollDesc) WaitWrite() error { - err := pd.pollServer.AddFD(pd, 'w') - if err == nil { - err = <-pd.cw - } - return err -} - -func (pd *pollDesc) Evict() bool { - return pd.pollServer.Evict(pd) -} - -// Spread network FDs over several pollServers. - -var pollMaxN int -var pollservers []*pollServer -var startServersOnce []func() - -var canCancelIO = true // used for testing current package - -func sysInit() { - pollMaxN = runtime.NumCPU() - if pollMaxN > 8 { - pollMaxN = 8 // No improvement then. - } - pollservers = make([]*pollServer, pollMaxN) - startServersOnce = make([]func(), pollMaxN) - for i := 0; i < pollMaxN; i++ { - k := i - once := new(sync.Once) - startServersOnce[i] = func() { once.Do(func() { startServer(k) }) } - } -} - -func startServer(k int) { - p, err := newPollServer() - if err != nil { - panic(err) - } - pollservers[k] = p -} - -func (pd *pollDesc) Init(fd *netFD) error { - pollN := runtime.GOMAXPROCS(0) - if pollN > pollMaxN { - pollN = pollMaxN - } - k := fd.sysfd % pollN - startServersOnce[k]() - pd.sysfd = fd.sysfd - pd.pollServer = pollservers[k] - pd.cr = make(chan error, 1) - pd.cw = make(chan error, 1) - return nil -} - -// TODO(dfc) these unused error returns could be removed - -func setReadDeadline(fd *netFD, t time.Time) error { - fd.pd.rdeadline.setTime(t) - return nil -} - -func setWriteDeadline(fd *netFD, t time.Time) error { - fd.pd.wdeadline.setTime(t) - return nil -} - -func setDeadline(fd *netFD, t time.Time) error { - setReadDeadline(fd, t) - setWriteDeadline(fd, t) - return nil -} diff --git a/libgo/go/net/fd_posix_test.go b/libgo/go/net/fd_posix_test.go deleted file mode 100644 index 8be0335..0000000 --- a/libgo/go/net/fd_posix_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd windows - -package net - -import ( - "testing" - "time" -) - -var deadlineSetTimeTests = []struct { - input time.Time - expected int64 -}{ - {time.Time{}, 0}, - {time.Date(2009, 11, 10, 23, 00, 00, 00, time.UTC), 1257894000000000000}, // 2009-11-10 23:00:00 +0000 UTC -} - -func TestDeadlineSetTime(t *testing.T) { - for _, tt := range deadlineSetTimeTests { - var d deadline - d.setTime(tt.input) - actual := d.value() - expected := int64(0) - if !tt.input.IsZero() { - expected = tt.input.UnixNano() - } - if actual != expected { - t.Errorf("set/value failed: expected %v, actual %v", expected, actual) - } - } -} - -var deadlineExpiredTests = []struct { - deadline time.Time - expired bool -}{ - // note, times are relative to the start of the test run, not - // the start of TestDeadlineExpired - {time.Now().Add(5 * time.Minute), false}, - {time.Now().Add(-5 * time.Minute), true}, - {time.Time{}, false}, // no deadline set -} - -func TestDeadlineExpired(t *testing.T) { - for _, tt := range deadlineExpiredTests { - var d deadline - d.set(tt.deadline.UnixNano()) - expired := d.expired() - if expired != tt.expired { - t.Errorf("expire failed: expected %v, actual %v", tt.expired, expired) - } - } -} diff --git a/libgo/go/net/fd_unix.go b/libgo/go/net/fd_unix.go index 8c59bff..9ed4f75 100644 --- a/libgo/go/net/fd_unix.go +++ b/libgo/go/net/fd_unix.go @@ -2,70 +2,59 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package net import ( "io" "os" - "sync" + "runtime" + "sync/atomic" "syscall" "time" ) // Network file descriptor. type netFD struct { - // locking/lifetime of sysfd - sysmu sync.Mutex - sysref int - - // must lock both sysmu and pollDesc to write - // can lock either to read - closing bool + // locking/lifetime of sysfd + serialize access to Read and Write methods + fdmu fdMutex // immutable until Close sysfd int family int sotype int isConnected bool - sysfile *os.File net string laddr Addr raddr Addr - // serialize access to Read and Write methods - rio, wio sync.Mutex - // wait server pd pollDesc } -func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) { - ra, err := resolveAddr("dial", net, addr, deadline) - if err != nil { - return nil, err - } - return dial(net, addr, localAddr, ra, deadline) +func sysInit() { } -func newFD(fd, family, sotype int, net string) (*netFD, error) { - netfd := &netFD{ - sysfd: fd, - family: family, - sotype: sotype, - net: net, - } - if err := netfd.pd.Init(netfd); err != nil { - return nil, err +func dial(network string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) { + return dialer(deadline) +} + +func newFD(sysfd, family, sotype int, net string) (*netFD, error) { + return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net}, nil +} + +func (fd *netFD) init() error { + if err := fd.pd.Init(fd); err != nil { + return err } - return netfd, nil + return nil } func (fd *netFD) setAddr(laddr, raddr Addr) { fd.laddr = laddr fd.raddr = raddr - fd.sysfile = os.NewFile(uintptr(fd.sysfd), fd.net) + runtime.SetFinalizer(fd, (*netFD).Close) } func (fd *netFD) name() string { @@ -80,8 +69,9 @@ func (fd *netFD) name() string { } func (fd *netFD) connect(la, ra syscall.Sockaddr) error { - fd.wio.Lock() - defer fd.wio.Unlock() + // Do not need to call fd.writeLock here, + // because fd is not yet accessible to user, + // so no concurrent operations are possible. if err := fd.pd.PrepareWrite(); err != nil { return err } @@ -100,48 +90,69 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error { return nil } +func (fd *netFD) destroy() { + // Poller may want to unregister fd in readiness notification mechanism, + // so this must be executed before closesocket. + fd.pd.Close() + closesocket(fd.sysfd) + fd.sysfd = -1 + runtime.SetFinalizer(fd, nil) +} + // Add a reference to this fd. -// If closing==true, pollDesc must be locked; mark the fd as closing. // Returns an error if the fd cannot be used. -func (fd *netFD) incref(closing bool) error { - fd.sysmu.Lock() - if fd.closing { - fd.sysmu.Unlock() +func (fd *netFD) incref() error { + if !fd.fdmu.Incref() { return errClosing } - fd.sysref++ - if closing { - fd.closing = true - } - fd.sysmu.Unlock() return nil } -// Remove a reference to this FD and close if we've been asked to do so (and -// there are no references left. +// Remove a reference to this FD and close if we've been asked to do so +// (and there are no references left). func (fd *netFD) decref() { - fd.sysmu.Lock() - fd.sysref-- - if fd.closing && fd.sysref == 0 { - // Poller may want to unregister fd in readiness notification mechanism, - // so this must be executed before sysfile.Close(). - fd.pd.Close() - if fd.sysfile != nil { - fd.sysfile.Close() - fd.sysfile = nil - } else { - closesocket(fd.sysfd) - } - fd.sysfd = -1 + if fd.fdmu.Decref() { + fd.destroy() + } +} + +// Add a reference to this fd and lock for reading. +// Returns an error if the fd cannot be used. +func (fd *netFD) readLock() error { + if !fd.fdmu.RWLock(true) { + return errClosing + } + return nil +} + +// Unlock for reading and remove a reference to this FD. +func (fd *netFD) readUnlock() { + if fd.fdmu.RWUnlock(true) { + fd.destroy() + } +} + +// Add a reference to this fd and lock for writing. +// Returns an error if the fd cannot be used. +func (fd *netFD) writeLock() error { + if !fd.fdmu.RWLock(false) { + return errClosing + } + return nil +} + +// Unlock for writing and remove a reference to this FD. +func (fd *netFD) writeUnlock() { + if fd.fdmu.RWUnlock(false) { + fd.destroy() } - fd.sysmu.Unlock() } func (fd *netFD) Close() error { fd.pd.Lock() // needed for both fd.incref(true) and pollDesc.Evict - if err := fd.incref(true); err != nil { + if !fd.fdmu.IncrefAndClose() { fd.pd.Unlock() - return err + return errClosing } // Unblock any I/O. Once it all unblocks and returns, // so that it cannot be referring to fd.sysfd anymore, @@ -158,7 +169,7 @@ func (fd *netFD) Close() error { } func (fd *netFD) shutdown(how int) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() @@ -178,12 +189,10 @@ func (fd *netFD) CloseWrite() error { } func (fd *netFD) Read(p []byte) (n int, err error) { - fd.rio.Lock() - defer fd.rio.Unlock() - if err := fd.incref(false); err != nil { + if err := fd.readLock(); err != nil { return 0, err } - defer fd.decref() + defer fd.readUnlock() if err := fd.pd.PrepareRead(); err != nil { return 0, &OpError{"read", fd.net, fd.raddr, err} } @@ -207,12 +216,10 @@ func (fd *netFD) Read(p []byte) (n int, err error) { } func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { - fd.rio.Lock() - defer fd.rio.Unlock() - if err := fd.incref(false); err != nil { + if err := fd.readLock(); err != nil { return 0, nil, err } - defer fd.decref() + defer fd.readUnlock() if err := fd.pd.PrepareRead(); err != nil { return 0, nil, &OpError{"read", fd.net, fd.laddr, err} } @@ -236,12 +243,10 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { } func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { - fd.rio.Lock() - defer fd.rio.Unlock() - if err := fd.incref(false); err != nil { + if err := fd.readLock(); err != nil { return 0, 0, 0, nil, err } - defer fd.decref() + defer fd.readUnlock() if err := fd.pd.PrepareRead(); err != nil { return 0, 0, 0, nil, &OpError{"read", fd.net, fd.laddr, err} } @@ -272,12 +277,10 @@ func chkReadErr(n int, err error, fd *netFD) error { } func (fd *netFD) Write(p []byte) (nn int, err error) { - fd.wio.Lock() - defer fd.wio.Unlock() - if err := fd.incref(false); err != nil { + if err := fd.writeLock(); err != nil { return 0, err } - defer fd.decref() + defer fd.writeUnlock() if err := fd.pd.PrepareWrite(); err != nil { return 0, &OpError{"write", fd.net, fd.raddr, err} } @@ -311,12 +314,10 @@ func (fd *netFD) Write(p []byte) (nn int, err error) { } func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { - fd.wio.Lock() - defer fd.wio.Unlock() - if err := fd.incref(false); err != nil { + if err := fd.writeLock(); err != nil { return 0, err } - defer fd.decref() + defer fd.writeUnlock() if err := fd.pd.PrepareWrite(); err != nil { return 0, &OpError{"write", fd.net, fd.raddr, err} } @@ -338,12 +339,10 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) { } func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { - fd.wio.Lock() - defer fd.wio.Unlock() - if err := fd.incref(false); err != nil { + if err := fd.writeLock(); err != nil { return 0, 0, err } - defer fd.decref() + defer fd.writeUnlock() if err := fd.pd.PrepareWrite(); err != nil { return 0, 0, &OpError{"write", fd.net, fd.raddr, err} } @@ -366,12 +365,10 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob } func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err error) { - fd.rio.Lock() - defer fd.rio.Unlock() - if err := fd.incref(false); err != nil { + if err := fd.readLock(); err != nil { return nil, err } - defer fd.decref() + defer fd.readUnlock() var s int var rsa syscall.Sockaddr @@ -399,20 +396,68 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e closesocket(s) return nil, err } + if err = netfd.init(); err != nil { + fd.Close() + return nil, err + } lsa, _ := syscall.Getsockname(netfd.sysfd) netfd.setAddr(toAddr(lsa), toAddr(rsa)) return netfd, nil } -func (fd *netFD) dup() (f *os.File, err error) { +// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used. +// If the kernel doesn't support it, this is set to 0. +var tryDupCloexec = int32(1) + +func dupCloseOnExec(fd int) (newfd int, err error) { + if atomic.LoadInt32(&tryDupCloexec) == 1 { + r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0) + if runtime.GOOS == "darwin" && e1 == syscall.EBADF { + // On OS X 10.6 and below (but we only support + // >= 10.6), F_DUPFD_CLOEXEC is unsupported + // and fcntl there falls back (undocumented) + // to doing an ioctl instead, returning EBADF + // in this case because fd is not of the + // expected device fd type. Treat it as + // EINVAL instead, so we fall back to the + // normal dup path. + // TODO: only do this on 10.6 if we can detect 10.6 + // cheaply. + e1 = syscall.EINVAL + } + switch e1 { + case 0: + return int(r0), nil + case syscall.EINVAL: + // Old kernel. Fall back to the portable way + // from now on. + atomic.StoreInt32(&tryDupCloexec, 0) + default: + return -1, e1 + } + } + return dupCloseOnExecOld(fd) +} + +// dupCloseOnExecUnixOld is the traditional way to dup an fd and +// set its O_CLOEXEC bit, using two system calls. +func dupCloseOnExecOld(fd int) (newfd int, err error) { syscall.ForkLock.RLock() - ns, err := syscall.Dup(fd.sysfd) + defer syscall.ForkLock.RUnlock() + newfd, err = syscall.Dup(fd) + if err != nil { + return -1, err + } + syscall.CloseOnExec(newfd) + return +} + +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} } - syscall.CloseOnExec(ns) - syscall.ForkLock.RUnlock() // We want blocking mode for the new fd, hence the double negative. // This also puts the old fd into blocking mode, meaning that @@ -428,3 +473,10 @@ func (fd *netFD) dup() (f *os.File, err error) { func closesocket(s int) error { return syscall.Close(s) } + +func skipRawSocketTests() (skip bool, skipmsg string, err error) { + if os.Getuid() != 0 { + return true, "skipping test; must be root", nil + } + return false, "", nil +} diff --git a/libgo/go/net/fd_unix_test.go b/libgo/go/net/fd_unix_test.go index 664ef1b..65d3e69 100644 --- a/libgo/go/net/fd_unix_test.go +++ b/libgo/go/net/fd_unix_test.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package net diff --git a/libgo/go/net/fd_windows.go b/libgo/go/net/fd_windows.go index fefd174..64d56c7 100644 --- a/libgo/go/net/fd_windows.go +++ b/libgo/go/net/fd_windows.go @@ -15,7 +15,10 @@ import ( "unsafe" ) -var initErr error +var ( + initErr error + ioSync uint64 +) // CancelIo Windows API cancels all outstanding IO for a particular // socket on current thread. To overcome that limitation, we run @@ -27,7 +30,11 @@ var initErr error // package uses CancelIoEx API, if present, otherwise it fallback // to CancelIo. -var canCancelIO bool // determines if CancelIoEx API is present +var ( + canCancelIO bool // determines if CancelIoEx API is present + skipSyncNotif bool + hasLoadSetFileCompletionNotificationModes bool +) func sysInit() { var d syscall.WSAData @@ -40,6 +47,27 @@ func sysInit() { lookupPort = newLookupPort lookupIP = newLookupIP } + + hasLoadSetFileCompletionNotificationModes = syscall.LoadSetFileCompletionNotificationModes() == nil + if hasLoadSetFileCompletionNotificationModes { + // It's not safe to use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS if non IFS providers are installed: + // http://support.microsoft.com/kb/2568167 + skipSyncNotif = true + protos := [2]int32{syscall.IPPROTO_TCP, 0} + var buf [32]syscall.WSAProtocolInfo + len := uint32(unsafe.Sizeof(buf)) + n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len) + if err != nil { + skipSyncNotif = false + } else { + for i := int32(0); i < n; i++ { + if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 { + skipSyncNotif = false + break + } + } + } + } } func closesocket(s syscall.Handle) error { @@ -47,128 +75,62 @@ func closesocket(s syscall.Handle) error { } func canUseConnectEx(net string) bool { - if net == "udp" || net == "udp4" || net == "udp6" { + switch net { + case "udp", "udp4", "udp6", "ip", "ip4", "ip6": // ConnectEx windows API does not support connectionless sockets. return false } return syscall.LoadConnectEx() == nil } -func resolveAndDial(net, addr string, localAddr Addr, deadline time.Time) (Conn, error) { +func dial(net string, ra Addr, dialer func(time.Time) (Conn, error), deadline time.Time) (Conn, error) { if !canUseConnectEx(net) { // Use the relatively inefficient goroutine-racing // implementation of DialTimeout. - return resolveAndDialChannel(net, addr, localAddr, deadline) - } - ra, err := resolveAddr("dial", net, addr, deadline) - if err != nil { - return nil, err + return dialChannel(net, ra, dialer, deadline) } - return dial(net, addr, localAddr, ra, deadline) + return dialer(deadline) } -// Interface for all IO operations. -type anOpIface interface { - Op() *anOp - Name() string - Submit() error -} - -// IO completion result parameters. -type ioResult struct { - qty uint32 - err error -} - -// anOp implements functionality common to all IO operations. -type anOp struct { +// operation contains superset of data necessary to perform all async IO. +type operation struct { // Used by IOCP interface, it must be first field // of the struct, as our code rely on it. o syscall.Overlapped - resultc chan ioResult - errnoc chan error - fd *netFD -} + // fields used by runtime.netpoll + runtimeCtx uintptr + mode int32 + errno int32 + qty uint32 -func (o *anOp) Init(fd *netFD, mode int) { - o.fd = fd - var i int - if mode == 'r' { - i = 0 - } else { - i = 1 - } - if fd.resultc[i] == nil { - fd.resultc[i] = make(chan ioResult, 1) - } - o.resultc = fd.resultc[i] - if fd.errnoc[i] == nil { - fd.errnoc[i] = make(chan error) - } - o.errnoc = fd.errnoc[i] + // fields used only by net package + fd *netFD + errc chan error + buf syscall.WSABuf + sa syscall.Sockaddr + rsa *syscall.RawSockaddrAny + rsan int32 + handle syscall.Handle + flags uint32 } -func (o *anOp) Op() *anOp { - return o -} - -// bufOp is used by IO operations that read / write -// data from / to client buffer. -type bufOp struct { - anOp - buf syscall.WSABuf -} - -func (o *bufOp) Init(fd *netFD, buf []byte, mode int) { - o.anOp.Init(fd, mode) +func (o *operation) InitBuf(buf []byte) { o.buf.Len = uint32(len(buf)) - if len(buf) == 0 { - o.buf.Buf = nil - } else { + o.buf.Buf = nil + if len(buf) != 0 { o.buf.Buf = (*byte)(unsafe.Pointer(&buf[0])) } } -// resultSrv will retrieve all IO completion results from -// iocp and send them to the correspondent waiting client -// goroutine via channel supplied in the request. -type resultSrv struct { - iocp syscall.Handle -} - -func runtime_blockingSyscallHint() - -func (s *resultSrv) Run() { - var o *syscall.Overlapped - var key uint32 - var r ioResult - for { - r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, 0) - if r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil { - runtime_blockingSyscallHint() - r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE) - } - switch { - case r.err == nil: - // Dequeued successfully completed IO packet. - case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil: - // Wait has timed out (should not happen now, but might be used in the future). - panic("GetQueuedCompletionStatus timed out") - case o == nil: - // Failed to dequeue anything -> report the error. - panic("GetQueuedCompletionStatus failed " + r.err.Error()) - default: - // Dequeued failed IO packet. - } - (*anOp)(unsafe.Pointer(o)).resultc <- r - } -} - // ioSrv executes net IO requests. type ioSrv struct { - submchan chan anOpIface // submit IO requests - canchan chan anOpIface // cancel IO requests + req chan ioSrvReq +} + +type ioSrvReq struct { + o *operation + submit func(o *operation) error // if nil, cancel the operation } // ProcessRemoteIO will execute submit IO requests on behalf @@ -179,192 +141,182 @@ type ioSrv struct { func (s *ioSrv) ProcessRemoteIO() { runtime.LockOSThread() defer runtime.UnlockOSThread() - for { - select { - case o := <-s.submchan: - o.Op().errnoc <- o.Submit() - case o := <-s.canchan: - o.Op().errnoc <- syscall.CancelIo(syscall.Handle(o.Op().fd.sysfd)) + for r := range s.req { + if r.submit != nil { + r.o.errc <- r.submit(r.o) + } else { + r.o.errc <- syscall.CancelIo(r.o.fd.sysfd) } } } -// ExecIO executes a single IO operation oi. It submits and cancels +// ExecIO executes a single IO operation o. It submits and cancels // IO in the current thread for systems where Windows CancelIoEx API // is available. Alternatively, it passes the request onto -// a special goroutine and waits for completion or cancels request. -// deadline is unix nanos. -func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) { - var err error - o := oi.Op() - // Calculate timeout delta. - var delta int64 - if deadline != 0 { - delta = deadline - time.Now().UnixNano() - if delta <= 0 { - return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout} - } +// runtime netpoll and waits for completion or cancels request. +func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) error) (int, error) { + fd := o.fd + // Notify runtime netpoll about starting IO. + err := fd.pd.Prepare(int(o.mode)) + if err != nil { + return 0, &OpError{name, fd.net, fd.laddr, err} } // Start IO. if canCancelIO { - err = oi.Submit() + err = submit(o) } else { // Send request to a special dedicated thread, // so it can stop the IO with CancelIO later. - s.submchan <- oi - err = <-o.errnoc + s.req <- ioSrvReq{o, submit} + err = <-o.errc } switch err { case nil: - // IO completed immediately, but we need to get our completion message anyway. + // IO completed immediately + if o.fd.skipSyncNotif { + // No completion message will follow, so return immediately. + return int(o.qty), nil + } + // Need to get our completion message anyway. case syscall.ERROR_IO_PENDING: // IO started, and we have to wait for its completion. err = nil default: - return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err} - } - // Setup timer, if deadline is given. - var timer <-chan time.Time - if delta > 0 { - t := time.NewTimer(time.Duration(delta) * time.Nanosecond) - defer t.Stop() - timer = t.C + return 0, &OpError{name, fd.net, fd.laddr, err} } // Wait for our request to complete. - var r ioResult - var cancelled, timeout bool - select { - case r = <-o.resultc: - case <-timer: - cancelled = true - timeout = true - case <-o.fd.closec: - cancelled = true - } - if cancelled { - // Cancel it. - if canCancelIO { - err := syscall.CancelIoEx(syscall.Handle(o.Op().fd.sysfd), &o.o) - // Assuming ERROR_NOT_FOUND is returned, if IO is completed. - if err != nil && err != syscall.ERROR_NOT_FOUND { - // TODO(brainman): maybe do something else, but panic. - panic(err) - } - } else { - s.canchan <- oi - <-o.errnoc - } - // Wait for IO to be canceled or complete successfully. - r = <-o.resultc - if r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled - if timeout { - r.err = errTimeout - } else { - r.err = errClosing - } + err = fd.pd.Wait(int(o.mode)) + if err == nil { + // All is good. Extract our IO results and return. + if o.errno != 0 { + err = syscall.Errno(o.errno) + return 0, &OpError{name, fd.net, fd.laddr, err} } + return int(o.qty), nil + } + // IO is interrupted by "close" or "timeout" + netpollErr := err + switch netpollErr { + case errClosing, errTimeout: + // will deal with those. + default: + panic("net: unexpected runtime.netpoll error: " + netpollErr.Error()) } - if r.err != nil { - err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err} + // Cancel our request. + if canCancelIO { + err := syscall.CancelIoEx(fd.sysfd, &o.o) + // Assuming ERROR_NOT_FOUND is returned, if IO is completed. + if err != nil && err != syscall.ERROR_NOT_FOUND { + // TODO(brainman): maybe do something else, but panic. + panic(err) + } + } else { + s.req <- ioSrvReq{o, nil} + <-o.errc + } + // Wait for cancellation to complete. + fd.pd.WaitCanceled(int(o.mode)) + if o.errno != 0 { + err = syscall.Errno(o.errno) + if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled + err = netpollErr + } + return 0, &OpError{name, fd.net, fd.laddr, err} } - return int(r.qty), err + // We issued cancellation request. But, it seems, IO operation succeeded + // before cancellation request run. We need to treat IO operation as + // succeeded (the bytes are actually sent/recv from network). + return int(o.qty), nil } // Start helper goroutines. -var resultsrv *resultSrv -var iosrv *ioSrv +var rsrv, wsrv *ioSrv var onceStartServer sync.Once func startServer() { - resultsrv = new(resultSrv) - var err error - resultsrv.iocp, err = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1) - if err != nil { - panic("CreateIoCompletionPort: " + err.Error()) - } - go resultsrv.Run() - - iosrv = new(ioSrv) + rsrv = new(ioSrv) + wsrv = new(ioSrv) if !canCancelIO { - // Only CancelIo API is available. Lets start special goroutine - // locked to an OS thread, that both starts and cancels IO. - iosrv.submchan = make(chan anOpIface) - iosrv.canchan = make(chan anOpIface) - go iosrv.ProcessRemoteIO() + // Only CancelIo API is available. Lets start two special goroutines + // locked to an OS thread, that both starts and cancels IO. One will + // process read requests, while other will do writes. + rsrv.req = make(chan ioSrvReq) + go rsrv.ProcessRemoteIO() + wsrv.req = make(chan ioSrvReq) + go wsrv.ProcessRemoteIO() } } // Network file descriptor. type netFD struct { - // locking/lifetime of sysfd - sysmu sync.Mutex - sysref int - closing bool + // locking/lifetime of sysfd + serialize access to Read and Write methods + fdmu fdMutex // immutable until Close - sysfd syscall.Handle - family int - sotype int - isConnected bool - net string - laddr Addr - raddr Addr - resultc [2]chan ioResult // read/write completion results - errnoc [2]chan error // read/write submit or cancel operation errors - closec chan bool // used by Close to cancel pending IO + sysfd syscall.Handle + family int + sotype int + isConnected bool + skipSyncNotif bool + net string + laddr Addr + raddr Addr - // serialize access to Read and Write methods - rio, wio sync.Mutex + rop operation // read operation + wop operation // write operation - // read and write deadlines - rdeadline, wdeadline deadline + // wait server + pd pollDesc } -func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD { - netfd := &netFD{ - sysfd: fd, - family: family, - sotype: sotype, - net: net, - closec: make(chan bool), - } - return netfd -} - -func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) { +func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) { if initErr != nil { return nil, initErr } onceStartServer.Do(startServer) - // Associate our socket with resultsrv.iocp. - if _, err := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); err != nil { - return nil, err + return &netFD{sysfd: sysfd, family: family, sotype: sotype, net: net}, nil +} + +func (fd *netFD) init() error { + if err := fd.pd.Init(fd); err != nil { + return err + } + if hasLoadSetFileCompletionNotificationModes { + // We do not use events, so we can skip them always. + flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE) + // It's not safe to skip completion notifications for UDP: + // http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx + if skipSyncNotif && fd.net == "tcp" { + flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS + } + err := syscall.SetFileCompletionNotificationModes(fd.sysfd, flags) + if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 { + fd.skipSyncNotif = true + } } - return allocFD(fd, family, proto, net), nil + fd.rop.mode = 'r' + fd.wop.mode = 'w' + fd.rop.fd = fd + fd.wop.fd = fd + fd.rop.runtimeCtx = fd.pd.runtimeCtx + fd.wop.runtimeCtx = fd.pd.runtimeCtx + if !canCancelIO { + fd.rop.errc = make(chan error) + fd.wop.errc = make(chan error) + } + return nil } func (fd *netFD) setAddr(laddr, raddr Addr) { fd.laddr = laddr fd.raddr = raddr - runtime.SetFinalizer(fd, (*netFD).closesocket) -} - -// Make new connection. - -type connectOp struct { - anOp - ra syscall.Sockaddr -} - -func (o *connectOp) Submit() error { - return syscall.ConnectEx(o.fd.sysfd, o.ra, nil, 0, nil, &o.o) -} - -func (o *connectOp) Name() string { - return "ConnectEx" + runtime.SetFinalizer(fd, (*netFD).Close) } func (fd *netFD) connect(la, ra syscall.Sockaddr) error { + // Do not need to call fd.writeLock here, + // because fd is not yet accessible to user, + // so no concurrent operations are possible. if !canUseConnectEx(fd.net) { return syscall.Connect(fd.sysfd, ra) } @@ -383,10 +335,11 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error { } } // Call ConnectEx API. - var o connectOp - o.Init(fd, 'w') - o.ra = ra - _, err := iosrv.ExecIO(&o, fd.wdeadline.value()) + o := &fd.wop + o.sa = ra + _, err := wsrv.ExecIO(o, "ConnectEx", func(o *operation) error { + return syscall.ConnectEx(o.fd.sysfd, o.sa, nil, 0, nil, &o.o) + }) if err != nil { return err } @@ -394,61 +347,80 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error { return syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) } +func (fd *netFD) destroy() { + if fd.sysfd == syscall.InvalidHandle { + return + } + // Poller may want to unregister fd in readiness notification mechanism, + // so this must be executed before closesocket. + fd.pd.Close() + closesocket(fd.sysfd) + fd.sysfd = syscall.InvalidHandle + // no need for a finalizer anymore + runtime.SetFinalizer(fd, nil) +} + // Add a reference to this fd. -// If closing==true, mark the fd as closing. // Returns an error if the fd cannot be used. -func (fd *netFD) incref(closing bool) error { - if fd == nil { +func (fd *netFD) incref() error { + if !fd.fdmu.Incref() { return errClosing } - fd.sysmu.Lock() - if fd.closing { - fd.sysmu.Unlock() - return errClosing + return nil +} + +// Remove a reference to this FD and close if we've been asked to do so +// (and there are no references left). +func (fd *netFD) decref() { + if fd.fdmu.Decref() { + fd.destroy() } - fd.sysref++ - if closing { - fd.closing = true +} + +// Add a reference to this fd and lock for reading. +// Returns an error if the fd cannot be used. +func (fd *netFD) readLock() error { + if !fd.fdmu.RWLock(true) { + return errClosing } - closing = fd.closing - fd.sysmu.Unlock() return nil } -// Remove a reference to this FD and close if we've been asked to do so (and -// there are no references left. -func (fd *netFD) decref() { - if fd == nil { - return +// Unlock for reading and remove a reference to this FD. +func (fd *netFD) readUnlock() { + if fd.fdmu.RWUnlock(true) { + fd.destroy() + } +} + +// Add a reference to this fd and lock for writing. +// Returns an error if the fd cannot be used. +func (fd *netFD) writeLock() error { + if !fd.fdmu.RWLock(false) { + return errClosing } - fd.sysmu.Lock() - fd.sysref-- - if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle { - closesocket(fd.sysfd) - fd.sysfd = syscall.InvalidHandle - // no need for a finalizer anymore - runtime.SetFinalizer(fd, nil) + return nil +} + +// Unlock for writing and remove a reference to this FD. +func (fd *netFD) writeUnlock() { + if fd.fdmu.RWUnlock(false) { + fd.destroy() } - fd.sysmu.Unlock() } func (fd *netFD) Close() error { - if err := fd.incref(true); err != nil { - return err + if !fd.fdmu.IncrefAndClose() { + return errClosing } - defer fd.decref() // unblock pending reader and writer - close(fd.closec) - // wait for both reader and writer to exit - fd.rio.Lock() - defer fd.rio.Unlock() - fd.wio.Lock() - defer fd.wio.Unlock() + fd.pd.Evict() + fd.decref() return nil } func (fd *netFD) shutdown(how int) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() @@ -467,72 +439,42 @@ func (fd *netFD) CloseWrite() error { return fd.shutdown(syscall.SHUT_WR) } -func (fd *netFD) closesocket() error { - return closesocket(fd.sysfd) -} - -// Read from network. - -type readOp struct { - bufOp -} - -func (o *readOp) Submit() error { - var d, f uint32 - return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil) -} - -func (o *readOp) Name() string { - return "WSARecv" -} - func (fd *netFD) Read(buf []byte) (int, error) { - if err := fd.incref(false); err != nil { + if err := fd.readLock(); err != nil { return 0, err } - defer fd.decref() - fd.rio.Lock() - defer fd.rio.Unlock() - var o readOp - o.Init(fd, buf, 'r') - n, err := iosrv.ExecIO(&o, fd.rdeadline.value()) + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err := rsrv.ExecIO(o, "WSARecv", func(o *operation) error { + return syscall.WSARecv(o.fd.sysfd, &o.buf, 1, &o.qty, &o.flags, &o.o, nil) + }) if err == nil && n == 0 { err = io.EOF } + if raceenabled { + raceAcquire(unsafe.Pointer(&ioSync)) + } return n, err } -// ReadFrom from network. - -type readFromOp struct { - bufOp - rsa syscall.RawSockaddrAny - rsan int32 -} - -func (o *readFromOp) Submit() error { - var d, f uint32 - return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &o.rsan, &o.o, nil) -} - -func (o *readFromOp) Name() string { - return "WSARecvFrom" -} - func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { if len(buf) == 0 { return 0, nil, nil } - if err := fd.incref(false); err != nil { + if err := fd.readLock(); err != nil { return 0, nil, err } - defer fd.decref() - fd.rio.Lock() - defer fd.rio.Unlock() - var o readFromOp - o.Init(fd, buf, 'r') - o.rsan = int32(unsafe.Sizeof(o.rsa)) - n, err = iosrv.ExecIO(&o, fd.rdeadline.value()) + defer fd.readUnlock() + o := &fd.rop + o.InitBuf(buf) + n, err = rsrv.ExecIO(o, "WSARecvFrom", func(o *operation) error { + if o.rsa == nil { + o.rsa = new(syscall.RawSockaddrAny) + } + o.rsan = int32(unsafe.Sizeof(*o.rsa)) + return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &o.qty, &o.flags, o.rsa, &o.rsan, &o.o, nil) + }) if err != nil { return 0, nil, err } @@ -540,89 +482,42 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) { return } -// Write to network. - -type writeOp struct { - bufOp -} - -func (o *writeOp) Submit() error { - var d uint32 - return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil) -} - -func (o *writeOp) Name() string { - return "WSASend" -} - func (fd *netFD) Write(buf []byte) (int, error) { - if err := fd.incref(false); err != nil { + if err := fd.writeLock(); err != nil { return 0, err } - defer fd.decref() - fd.wio.Lock() - defer fd.wio.Unlock() - var o writeOp - o.Init(fd, buf, 'w') - return iosrv.ExecIO(&o, fd.wdeadline.value()) -} - -// WriteTo to network. - -type writeToOp struct { - bufOp - sa syscall.Sockaddr -} - -func (o *writeToOp) Submit() error { - var d uint32 - return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil) -} - -func (o *writeToOp) Name() string { - return "WSASendto" + defer fd.writeUnlock() + if raceenabled { + raceReleaseMerge(unsafe.Pointer(&ioSync)) + } + o := &fd.wop + o.InitBuf(buf) + return wsrv.ExecIO(o, "WSASend", func(o *operation) error { + return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &o.qty, 0, &o.o, nil) + }) } func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) { if len(buf) == 0 { return 0, nil } - if err := fd.incref(false); err != nil { + if err := fd.writeLock(); err != nil { return 0, err } - defer fd.decref() - fd.wio.Lock() - defer fd.wio.Unlock() - var o writeToOp - o.Init(fd, buf, 'w') + defer fd.writeUnlock() + o := &fd.wop + o.InitBuf(buf) o.sa = sa - return iosrv.ExecIO(&o, fd.wdeadline.value()) -} - -// Accept new network connections. - -type acceptOp struct { - anOp - newsock syscall.Handle - attrs [2]syscall.RawSockaddrAny // space for local and remote address only -} - -func (o *acceptOp) Submit() error { - var d uint32 - l := uint32(unsafe.Sizeof(o.attrs[0])) - return syscall.AcceptEx(o.fd.sysfd, o.newsock, - (*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o) -} - -func (o *acceptOp) Name() string { - return "AcceptEx" + return wsrv.ExecIO(o, "WSASendto", func(o *operation) error { + return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &o.qty, 0, o.sa, &o.o, nil) + }) } func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { - if err := fd.incref(false); err != nil { + if err := fd.readLock(); err != nil { return nil, err } - defer fd.decref() + defer fd.readUnlock() // Get new socket. s, err := sysSocket(fd.family, fd.sotype, 0) @@ -631,43 +526,67 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) { } // Associate our new socket with IOCP. - onceStartServer.Do(startServer) - if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil { + netfd, err := newFD(s, fd.family, fd.sotype, fd.net) + if err != nil { closesocket(s) - return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err} + return nil, &OpError{"accept", fd.net, fd.laddr, err} + } + if err := netfd.init(); err != nil { + fd.Close() + return nil, err } // Submit accept request. - var o acceptOp - o.Init(fd, 'r') - o.newsock = s - _, err = iosrv.ExecIO(&o, fd.rdeadline.value()) + o := &fd.rop + o.handle = s + var rawsa [2]syscall.RawSockaddrAny + o.rsan = int32(unsafe.Sizeof(rawsa[0])) + _, err = rsrv.ExecIO(o, "AcceptEx", func(o *operation) error { + return syscall.AcceptEx(o.fd.sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o) + }) if err != nil { - closesocket(s) + netfd.Close() return nil, err } // Inherit properties of the listening socket. err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) if err != nil { - closesocket(s) + netfd.Close() return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err} } // Get local and peer addr out of AcceptEx buffer. var lrsa, rrsa *syscall.RawSockaddrAny var llen, rlen int32 - l := uint32(unsafe.Sizeof(*lrsa)) - syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&o.attrs[0])), - 0, l, l, &lrsa, &llen, &rrsa, &rlen) + syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])), + 0, uint32(o.rsan), uint32(o.rsan), &lrsa, &llen, &rrsa, &rlen) lsa, _ := lrsa.Sockaddr() rsa, _ := rrsa.Sockaddr() - netfd := allocFD(s, fd.family, fd.sotype, fd.net) netfd.setAddr(toAddr(lsa), toAddr(rsa)) return netfd, nil } +func skipRawSocketTests() (skip bool, skipmsg string, err error) { + // From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx: + // Note: To use a socket of type SOCK_RAW requires administrative privileges. + // Users running Winsock applications that use raw sockets must be a member of + // the Administrators group on the local computer, otherwise raw socket calls + // will fail with an error code of WSAEACCES. On Windows Vista and later, access + // for raw sockets is enforced at socket creation. In earlier versions of Windows, + // access for raw sockets is enforced during other socket operations. + s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, 0) + if err == syscall.WSAEACCES { + return true, "skipping test; no access to raw socket allowed", nil + } + if err != nil { + return true, "", err + } + defer syscall.Closesocket(s) + return false, "", nil +} + // Unimplemented functions. func (fd *netFD) dup() (*os.File, error) { diff --git a/libgo/go/net/file_unix.go b/libgo/go/net/file_unix.go index 4c8403e..8fe1b0e 100644 --- a/libgo/go/net/file_unix.go +++ b/libgo/go/net/file_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package net @@ -12,14 +12,11 @@ import ( ) func newFileFD(f *os.File) (*netFD, error) { - syscall.ForkLock.RLock() - fd, err := syscall.Dup(int(f.Fd())) + fd, err := dupCloseOnExec(int(f.Fd())) if err != nil { - syscall.ForkLock.RUnlock() return nil, os.NewSyscallError("dup", err) } - syscall.CloseOnExec(fd) - syscall.ForkLock.RUnlock() + if err = syscall.SetNonblock(fd, true); err != nil { closesocket(fd) return nil, err @@ -70,6 +67,10 @@ func newFileFD(f *os.File) (*netFD, error) { closesocket(fd) return nil, err } + if err := netfd.init(); err != nil { + netfd.Close() + return nil, err + } netfd.setAddr(laddr, raddr) return netfd, nil } diff --git a/libgo/go/net/http/cgi/child.go b/libgo/go/net/http/cgi/child.go index 100b8b7..45fc2e5 100644 --- a/libgo/go/net/http/cgi/child.go +++ b/libgo/go/net/http/cgi/child.go @@ -100,10 +100,21 @@ func RequestFromMap(params map[string]string) (*http.Request, error) { uriStr += "?" + s } } + + // There's apparently a de-facto standard for this. + // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636 + if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" { + r.TLS = &tls.ConnectionState{HandshakeComplete: true} + } + if r.Host != "" { - // Hostname is provided, so we can reasonably construct a URL, - // even if we have to assume 'http' for the scheme. - rawurl := "http://" + r.Host + uriStr + // Hostname is provided, so we can reasonably construct a URL. + rawurl := r.Host + uriStr + if r.TLS == nil { + rawurl = "http://" + rawurl + } else { + rawurl = "https://" + rawurl + } url, err := url.Parse(rawurl) if err != nil { return nil, errors.New("cgi: failed to parse host and REQUEST_URI into a URL: " + rawurl) @@ -120,12 +131,6 @@ func RequestFromMap(params map[string]string) (*http.Request, error) { r.URL = url } - // There's apparently a de-facto standard for this. - // http://docstore.mik.ua/orelly/linux/cgi/ch03_02.htm#ch03-35636 - if s := params["HTTPS"]; s == "on" || s == "ON" || s == "1" { - r.TLS = &tls.ConnectionState{HandshakeComplete: true} - } - // Request.RemoteAddr has its port set by Go's standard http // server, so we do here too. We don't have one, though, so we // use a dummy one. diff --git a/libgo/go/net/http/cgi/child_test.go b/libgo/go/net/http/cgi/child_test.go index 74e0680..075d841 100644 --- a/libgo/go/net/http/cgi/child_test.go +++ b/libgo/go/net/http/cgi/child_test.go @@ -21,7 +21,6 @@ func TestRequest(t *testing.T) { "REQUEST_URI": "/path?a=b", "CONTENT_LENGTH": "123", "CONTENT_TYPE": "text/xml", - "HTTPS": "1", "REMOTE_ADDR": "5.6.7.8", } req, err := RequestFromMap(env) @@ -58,14 +57,37 @@ func TestRequest(t *testing.T) { if req.Trailer == nil { t.Errorf("unexpected nil Trailer") } - if req.TLS == nil { - t.Errorf("expected non-nil TLS") + if req.TLS != nil { + t.Errorf("expected nil TLS") } if e, g := "5.6.7.8:0", req.RemoteAddr; e != g { t.Errorf("RemoteAddr: got %q; want %q", g, e) } } +func TestRequestWithTLS(t *testing.T) { + env := map[string]string{ + "SERVER_PROTOCOL": "HTTP/1.1", + "REQUEST_METHOD": "GET", + "HTTP_HOST": "example.com", + "HTTP_REFERER": "elsewhere", + "REQUEST_URI": "/path?a=b", + "CONTENT_TYPE": "text/xml", + "HTTPS": "1", + "REMOTE_ADDR": "5.6.7.8", + } + req, err := RequestFromMap(env) + if err != nil { + t.Fatalf("RequestFromMap: %v", err) + } + if g, e := req.URL.String(), "https://example.com/path?a=b"; e != g { + t.Errorf("expected URL %q; got %q", e, g) + } + if req.TLS == nil { + t.Errorf("expected non-nil TLS") + } +} + func TestRequestWithoutHost(t *testing.T) { env := map[string]string{ "SERVER_PROTOCOL": "HTTP/1.1", diff --git a/libgo/go/net/http/client.go b/libgo/go/net/http/client.go index a34d47b..22f2e86 100644 --- a/libgo/go/net/http/client.go +++ b/libgo/go/net/http/client.go @@ -74,8 +74,8 @@ type RoundTripper interface { // authentication, or cookies. // // RoundTrip should not modify the request, except for - // consuming the Body. The request's URL and Header fields - // are guaranteed to be initialized. + // consuming and closing the Body. The request's URL and + // Header fields are guaranteed to be initialized. RoundTrip(*Request) (*Response, error) } @@ -161,7 +161,9 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) { } if u := req.URL.User; u != nil { - req.Header.Set("Authorization", "Basic "+base64.URLEncoding.EncodeToString([]byte(u.String()))) + username := u.Username() + password, _ := u.Password() + req.Header.Set("Authorization", "Basic "+basicAuth(username, password)) } resp, err = t.RoundTrip(req) if err != nil { @@ -173,6 +175,16 @@ func send(req *Request, t RoundTripper) (resp *Response, err error) { return resp, nil } +// See 2 (end of page 4) http://www.ietf.org/rfc/rfc2617.txt +// "To receive authorization, the client sends the userid and password, +// separated by a single colon (":") character, within a base64 +// encoded string in the credentials." +// It is not meant to be urlencoded. +func basicAuth(username, password string) string { + auth := username + ":" + password + return base64.StdEncoding.EncodeToString([]byte(auth)) +} + // True if the specified HTTP status code is one for which the Get utility should // automatically redirect. func shouldRedirectGet(statusCode int) bool { @@ -335,6 +347,9 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro // Post issues a POST to the specified URL. // // 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. func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) { req, err := NewRequest("POST", url, body) if err != nil { diff --git a/libgo/go/net/http/client_test.go b/libgo/go/net/http/client_test.go index 73f1fe3..997d041 100644 --- a/libgo/go/net/http/client_test.go +++ b/libgo/go/net/http/client_test.go @@ -10,6 +10,7 @@ import ( "bytes" "crypto/tls" "crypto/x509" + "encoding/base64" "errors" "fmt" "io" @@ -665,6 +666,36 @@ func TestClientWithIncorrectTLSServerName(t *testing.T) { } } +// Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName +// when not empty. +// +// tls.Config.ServerName (non-empty, set to "example.com") takes +// precedence over "some-other-host.tld" which previously incorrectly +// took precedence. We don't actually connect to (or even resolve) +// "some-other-host.tld", though, because of the Transport.Dial hook. +// +// The httptest.Server has a cert with "example.com" as its name. +func TestTransportUsesTLSConfigServerName(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.ServerName = "example.com" // one of httptest's Server cert names + 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://some-other-host.tld/") + if err != nil { + t.Fatal(err) + } + res.Body.Close() +} + // Verify Response.ContentLength is populated. http://golang.org/issue/4126 func TestClientHeadContentLength(t *testing.T) { defer afterTest(t) @@ -700,3 +731,71 @@ func TestClientHeadContentLength(t *testing.T) { } } } + +func TestEmptyPasswordAuth(t *testing.T) { + defer afterTest(t) + gopher := "gopher" + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + auth := r.Header.Get("Authorization") + if strings.HasPrefix(auth, "Basic ") { + encoded := auth[6:] + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + t.Fatal(err) + } + expected := gopher + ":" + s := string(decoded) + if expected != s { + t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected) + } + } else { + t.Errorf("Invalid auth %q", auth) + } + })) + defer ts.Close() + c := &Client{} + req, err := NewRequest("GET", ts.URL, nil) + if err != nil { + t.Fatal(err) + } + req.URL.User = url.User(gopher) + resp, err := c.Do(req) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() +} + +func TestBasicAuth(t *testing.T) { + defer afterTest(t) + tr := &recordingTransport{} + client := &Client{Transport: tr} + + url := "http://My%20User:My%20Pass@dummy.faketld/" + expected := "My User:My Pass" + client.Get(url) + + if tr.req.Method != "GET" { + t.Errorf("got method %q, want %q", tr.req.Method, "GET") + } + if tr.req.URL.String() != url { + t.Errorf("got URL %q, want %q", tr.req.URL.String(), url) + } + if tr.req.Header == nil { + t.Fatalf("expected non-nil request Header") + } + auth := tr.req.Header.Get("Authorization") + if strings.HasPrefix(auth, "Basic ") { + encoded := auth[6:] + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + t.Fatal(err) + } + s := string(decoded) + if expected != s { + t.Errorf("Invalid Authorization header. Got %q, wanted %q", s, expected) + } + } else { + t.Errorf("Invalid auth %q", auth) + } +} diff --git a/libgo/go/net/http/cookie.go b/libgo/go/net/http/cookie.go index 155b092..8b01c50 100644 --- a/libgo/go/net/http/cookie.go +++ b/libgo/go/net/http/cookie.go @@ -7,6 +7,8 @@ package http import ( "bytes" "fmt" + "log" + "net" "strconv" "strings" "time" @@ -139,12 +141,25 @@ func SetCookie(w ResponseWriter, cookie *Cookie) { // header (if other fields are set). func (c *Cookie) String() string { var b bytes.Buffer - fmt.Fprintf(&b, "%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) + fmt.Fprintf(&b, "%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value)) if len(c.Path) > 0 { - fmt.Fprintf(&b, "; Path=%s", sanitizeValue(c.Path)) + fmt.Fprintf(&b, "; Path=%s", sanitizeCookiePath(c.Path)) } if len(c.Domain) > 0 { - fmt.Fprintf(&b, "; Domain=%s", sanitizeValue(c.Domain)) + if validCookieDomain(c.Domain) { + // A c.Domain containing illegal characters is not + // sanitized but simply dropped which turns the cookie + // into a host-only cookie. A leading dot is okay + // but won't be sent. + d := c.Domain + if d[0] == '.' { + d = d[1:] + } + fmt.Fprintf(&b, "; Domain=%s", d) + } else { + log.Printf("net/http: invalid Cookie.Domain %q; dropping domain attribute", + c.Domain) + } } if c.Expires.Unix() > 0 { fmt.Fprintf(&b, "; Expires=%s", c.Expires.UTC().Format(time.RFC1123)) @@ -207,16 +222,122 @@ func readCookies(h Header, filter string) []*Cookie { return cookies } +// validCookieDomain returns wheter v is a valid cookie domain-value. +func validCookieDomain(v string) bool { + if isCookieDomainName(v) { + return true + } + if net.ParseIP(v) != nil && !strings.Contains(v, ":") { + return true + } + return false +} + +// isCookieDomainName returns whether s is a valid domain name or a valid +// domain name with a leading dot '.'. It is almost a direct copy of +// package net's isDomainName. +func isCookieDomainName(s string) bool { + if len(s) == 0 { + return false + } + if len(s) > 255 { + return false + } + + if s[0] == '.' { + // A cookie a domain attribute may start with a leading dot. + s = s[1:] + } + last := byte('.') + ok := false // Ok once we've seen a letter. + partlen := 0 + for i := 0; i < len(s); i++ { + c := s[i] + switch { + default: + return false + case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z': + // No '_' allowed here (in contrast to package net). + ok = true + partlen++ + case '0' <= c && c <= '9': + // fine + partlen++ + case c == '-': + // Byte before dash cannot be dot. + if last == '.' { + return false + } + partlen++ + case c == '.': + // Byte before dot cannot be dot, dash. + if last == '.' || last == '-' { + return false + } + if partlen > 63 || partlen == 0 { + return false + } + partlen = 0 + } + last = c + } + if last == '-' || partlen > 63 { + return false + } + + return ok +} + var cookieNameSanitizer = strings.NewReplacer("\n", "-", "\r", "-") -func sanitizeName(n string) string { +func sanitizeCookieName(n string) string { return cookieNameSanitizer.Replace(n) } -var cookieValueSanitizer = strings.NewReplacer("\n", " ", "\r", " ", ";", " ") +// http://tools.ietf.org/html/rfc6265#section-4.1.1 +// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) +// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E +// ; US-ASCII characters excluding CTLs, +// ; whitespace DQUOTE, comma, semicolon, +// ; and backslash +func sanitizeCookieValue(v string) string { + return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v) +} + +func validCookieValueByte(b byte) bool { + return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\' +} + +// path-av = "Path=" path-value +// path-value = <any CHAR except CTLs or ";"> +func sanitizeCookiePath(v string) string { + return sanitizeOrWarn("Cookie.Path", validCookiePathByte, v) +} -func sanitizeValue(v string) string { - return cookieValueSanitizer.Replace(v) +func validCookiePathByte(b byte) bool { + return 0x20 <= b && b < 0x7f && b != ';' +} + +func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string { + ok := true + for i := 0; i < len(v); i++ { + if valid(v[i]) { + continue + } + log.Printf("net/http: invalid byte %q in %s; dropping invalid bytes", v[i], fieldName) + ok = false + break + } + if ok { + return v + } + buf := make([]byte, 0, len(v)) + for i := 0; i < len(v); i++ { + if b := v[i]; valid(b) { + buf = append(buf, b) + } + } + return string(buf) } func unquoteCookieValue(v string) string { diff --git a/libgo/go/net/http/cookie_test.go b/libgo/go/net/http/cookie_test.go index f84f739..11b01cc 100644 --- a/libgo/go/net/http/cookie_test.go +++ b/libgo/go/net/http/cookie_test.go @@ -26,12 +26,28 @@ var writeSetCookiesTests = []struct { }, { &Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"}, - "cookie-3=three; Domain=.example.com", + "cookie-3=three; Domain=example.com", }, { &Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"}, "cookie-4=four; Path=/restricted/", }, + { + &Cookie{Name: "cookie-5", Value: "five", Domain: "wrong;bad.abc"}, + "cookie-5=five", + }, + { + &Cookie{Name: "cookie-6", Value: "six", Domain: "bad-.abc"}, + "cookie-6=six", + }, + { + &Cookie{Name: "cookie-7", Value: "seven", Domain: "127.0.0.1"}, + "cookie-7=seven; Domain=127.0.0.1", + }, + { + &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"}, + "cookie-8=eight", + }, } func TestWriteSetCookies(t *testing.T) { @@ -226,3 +242,34 @@ func TestReadCookies(t *testing.T) { } } } + +func TestCookieSanitizeValue(t *testing.T) { + tests := []struct { + in, want string + }{ + {"foo", "foo"}, + {"foo bar", "foobar"}, + {"\x00\x7e\x7f\x80", "\x7e"}, + {`"withquotes"`, "withquotes"}, + } + for _, tt := range tests { + if got := sanitizeCookieValue(tt.in); got != tt.want { + t.Errorf("sanitizeCookieValue(%q) = %q; want %q", tt.in, got, tt.want) + } + } +} + +func TestCookieSanitizePath(t *testing.T) { + tests := []struct { + in, want string + }{ + {"/path", "/path"}, + {"/path with space/", "/path with space/"}, + {"/just;no;semicolon\x00orstuff/", "/justnosemicolonorstuff/"}, + } + for _, tt := range tests { + if got := sanitizeCookiePath(tt.in); got != tt.want { + t.Errorf("sanitizeCookiePath(%q) = %q; want %q", tt.in, got, tt.want) + } + } +} diff --git a/libgo/go/net/http/cookiejar/jar.go b/libgo/go/net/http/cookiejar/jar.go index 5977d48..389ab58 100644 --- a/libgo/go/net/http/cookiejar/jar.go +++ b/libgo/go/net/http/cookiejar/jar.go @@ -142,7 +142,7 @@ func (e *entry) pathMatch(requestPath string) bool { return false } -// hasDotSuffix returns whether s ends in "."+suffix. +// hasDotSuffix reports whether s ends in "."+suffix. func hasDotSuffix(s, suffix string) bool { return len(s) > len(suffix) && s[len(s)-len(suffix)-1] == '.' && s[len(s)-len(suffix):] == suffix } @@ -316,7 +316,7 @@ func canonicalHost(host string) (string, error) { return toASCII(host) } -// hasPort returns whether host contains a port number. host may be a host +// hasPort reports whether host contains a port number. host may be a host // name, an IPv4 or an IPv6 address. func hasPort(host string) bool { colons := strings.Count(host, ":") @@ -357,7 +357,7 @@ func jarKey(host string, psl PublicSuffixList) string { return host[prevDot+1:] } -// isIP returns whether host is an IP address. +// isIP reports whether host is an IP address. func isIP(host string) bool { return net.ParseIP(host) != nil } @@ -380,7 +380,7 @@ func defaultPath(path string) string { // is compared to c.Expires to determine deletion of c. defPath and host are the // default-path and the canonical host name of the URL c was received from. // -// remove is whether the jar should delete this cookie, as it has already +// remove records whether the jar should delete this cookie, as it has already // expired with respect to now. In this case, e may be incomplete, but it will // be valid to call e.id (which depends on e's Name, Domain and Path). // diff --git a/libgo/go/net/http/doc.go b/libgo/go/net/http/doc.go index b6ae8b8..b1216e8 100644 --- a/libgo/go/net/http/doc.go +++ b/libgo/go/net/http/doc.go @@ -5,7 +5,7 @@ /* Package http provides HTTP client and server implementations. -Get, Head, Post, and PostForm make HTTP requests: +Get, Head, Post, and PostForm make HTTP (or HTTPS) requests: resp, err := http.Get("http://example.com/") ... diff --git a/libgo/go/net/http/example_test.go b/libgo/go/net/http/example_test.go index bc60df7..88b97d9 100644 --- a/libgo/go/net/http/example_test.go +++ b/libgo/go/net/http/example_test.go @@ -68,3 +68,21 @@ func ExampleStripPrefix() { // URL's path before the FileServer sees it: http.Handle("/tmpfiles/", http.StripPrefix("/tmpfiles/", http.FileServer(http.Dir("/tmp")))) } + +type apiHandler struct{} + +func (apiHandler) ServeHTTP(http.ResponseWriter, *http.Request) {} + +func ExampleServeMux_Handle() { + mux := http.NewServeMux() + mux.Handle("/api/", apiHandler{}) + mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { + // The "/" pattern matches everything, so we need to check + // that we're at the root here. + if req.URL.Path != "/" { + http.NotFound(w, req) + return + } + fmt.Fprintf(w, "Welcome to the home page!") + }) +} diff --git a/libgo/go/net/http/export_test.go b/libgo/go/net/http/export_test.go index 3fc2453..22b7f27 100644 --- a/libgo/go/net/http/export_test.go +++ b/libgo/go/net/http/export_test.go @@ -16,6 +16,8 @@ func NewLoggingConn(baseName string, c net.Conn) net.Conn { return newLoggingConn(baseName, c) } +var ExportAppendTime = appendTime + func (t *Transport) NumPendingRequestsForTesting() int { t.reqMu.Lock() defer t.reqMu.Unlock() @@ -48,6 +50,12 @@ func (t *Transport) IdleConnCountForTesting(cacheKey string) int { return len(conns) } +func (t *Transport) IdleConnChMapSizeForTesting() int { + t.idleMu.Lock() + defer t.idleMu.Unlock() + return len(t.idleConnCh) +} + func NewTestTimeoutHandler(handler Handler, ch <-chan time.Time) Handler { f := func() <-chan time.Time { return ch diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go index b6bea0d..8b32ca1 100644 --- a/libgo/go/net/http/fs.go +++ b/libgo/go/net/http/fs.go @@ -105,23 +105,31 @@ func dirList(w ResponseWriter, f File) { // // Note that *os.File implements the io.ReadSeeker interface. func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) { - size, err := content.Seek(0, os.SEEK_END) - if err != nil { - Error(w, "seeker can't seek", StatusInternalServerError) - return - } - _, err = content.Seek(0, os.SEEK_SET) - if err != nil { - Error(w, "seeker can't seek", StatusInternalServerError) - return + sizeFunc := func() (int64, error) { + size, err := content.Seek(0, os.SEEK_END) + if err != nil { + return 0, errSeeker + } + _, err = content.Seek(0, os.SEEK_SET) + if err != nil { + return 0, errSeeker + } + return size, nil } - serveContent(w, req, name, modtime, size, content) + serveContent(w, req, name, modtime, sizeFunc, content) } +// errSeeker is returned by ServeContent's sizeFunc when the content +// doesn't seek properly. The underlying Seeker's error text isn't +// included in the sizeFunc reply so it's not sent over HTTP to end +// users. +var errSeeker = errors.New("seeker can't seek") + // if name is empty, filename is unknown. (used for mime type, before sniffing) // if modtime.IsZero(), modtime is unknown. // content must be seeked to the beginning of the file. -func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, size int64, content io.ReadSeeker) { +// The sizeFunc is called at most once. Its error, if any, is sent in the HTTP response. +func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) { if checkLastModified(w, r, modtime) { return } @@ -132,16 +140,17 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, code := StatusOK - // If Content-Type isn't set, use the file's extension to find it. - ctype := w.Header().Get("Content-Type") - if ctype == "" { + // If Content-Type isn't set, use the file's extension to find it, but + // if the Content-Type is unset explicitly, do not sniff the type. + ctypes, haveType := w.Header()["Content-Type"] + var ctype string + if !haveType { ctype = mime.TypeByExtension(filepath.Ext(name)) if ctype == "" { // read a chunk to decide between utf-8 text and binary - var buf [1024]byte + var buf [sniffLen]byte n, _ := io.ReadFull(content, buf[:]) - b := buf[:n] - ctype = DetectContentType(b) + ctype = DetectContentType(buf[:n]) _, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file if err != nil { Error(w, "seeker can't seek", StatusInternalServerError) @@ -149,6 +158,14 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, } } w.Header().Set("Content-Type", ctype) + } else if len(ctypes) > 0 { + ctype = ctypes[0] + } + + size, err := sizeFunc() + if err != nil { + Error(w, err.Error(), StatusInternalServerError) + return } // handle Content-Range header. @@ -160,7 +177,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) return } - if sumRangesSize(ranges) >= size { + if sumRangesSize(ranges) > size { // The total number of bytes in all the ranges // is larger than the size of the file by // itself, so this is probably an attack, or a @@ -378,7 +395,8 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } // serverContent will check modification time - serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f) + sizeFunc := func() (int64, error) { return d.Size(), nil } + serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f) } // localRedirect gives a Moved Permanently response. diff --git a/libgo/go/net/http/fs_test.go b/libgo/go/net/http/fs_test.go index d389667..dd3e9fe 100644 --- a/libgo/go/net/http/fs_test.go +++ b/libgo/go/net/http/fs_test.go @@ -20,8 +20,10 @@ import ( "os/exec" "path" "path/filepath" + "reflect" "regexp" "runtime" + "strconv" "strings" "testing" "time" @@ -36,6 +38,8 @@ type wantRange struct { start, end int64 // range [start,end) } +var itoa = strconv.Itoa + var ServeFileRangeTests = []struct { r string code int @@ -50,7 +54,11 @@ var ServeFileRangeTests = []struct { {r: "bytes=0-0,-2", code: StatusPartialContent, ranges: []wantRange{{0, 1}, {testFileLen - 2, testFileLen}}}, {r: "bytes=0-1,5-8", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, 9}}}, {r: "bytes=0-1,5-", code: StatusPartialContent, ranges: []wantRange{{0, 2}, {5, testFileLen}}}, + {r: "bytes=5-1000", code: StatusPartialContent, ranges: []wantRange{{5, testFileLen}}}, {r: "bytes=0-,1-,2-,3-,4-", code: StatusOK}, // ignore wasteful range request + {r: "bytes=0-" + itoa(testFileLen-2), code: StatusPartialContent, ranges: []wantRange{{0, testFileLen - 1}}}, + {r: "bytes=0-" + itoa(testFileLen-1), code: StatusPartialContent, ranges: []wantRange{{0, testFileLen}}}, + {r: "bytes=0-" + itoa(testFileLen), code: StatusPartialContent, ranges: []wantRange{{0, testFileLen}}}, } func TestServeFile(t *testing.T) { @@ -259,6 +267,9 @@ func TestFileServerImplicitLeadingSlash(t *testing.T) { } func TestDirJoin(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("skipping test on windows") + } wfi, err := os.Stat("/etc/hosts") if err != nil { t.Skip("skipping test; no /etc/hosts file") @@ -309,24 +320,29 @@ func TestServeFileContentType(t *testing.T) { defer afterTest(t) const ctype = "icecream/chocolate" ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - if r.FormValue("override") == "1" { + switch r.FormValue("override") { + case "1": w.Header().Set("Content-Type", ctype) + case "2": + // Explicitly inhibit sniffing. + w.Header()["Content-Type"] = []string{} } ServeFile(w, r, "testdata/file") })) defer ts.Close() - get := func(override, want string) { + get := func(override string, want []string) { resp, err := Get(ts.URL + "?override=" + override) if err != nil { t.Fatal(err) } - if h := resp.Header.Get("Content-Type"); h != want { - t.Errorf("Content-Type mismatch: got %q, want %q", h, want) + if h := resp.Header["Content-Type"]; !reflect.DeepEqual(h, want) { + t.Errorf("Content-Type mismatch: got %v, want %v", h, want) } resp.Body.Close() } - get("0", "text/plain; charset=utf-8") - get("1", ctype) + get("0", []string{"text/plain; charset=utf-8"}) + get("1", []string{ctype}) + get("2", nil) } func TestServeFileMimeType(t *testing.T) { @@ -567,7 +583,10 @@ func TestServeContent(t *testing.T) { defer ts.Close() type testCase struct { - file string + // One of file or content must be set: + file string + content io.ReadSeeker + modtime time.Time serveETag string // optional serveContentType string // optional @@ -615,6 +634,14 @@ func TestServeContent(t *testing.T) { }, wantStatus: 304, }, + "not_modified_etag_no_seek": { + content: panicOnSeek{nil}, // should never be called + serveETag: `"foo"`, + reqHeader: map[string]string{ + "If-None-Match": `"foo"`, + }, + wantStatus: 304, + }, "range_good": { file: "testdata/style.css", serveETag: `"A"`, @@ -638,15 +665,21 @@ func TestServeContent(t *testing.T) { }, } for testName, tt := range tests { - f, err := os.Open(tt.file) - if err != nil { - t.Fatalf("test %q: %v", testName, err) + var content io.ReadSeeker + if tt.file != "" { + f, err := os.Open(tt.file) + if err != nil { + t.Fatalf("test %q: %v", testName, err) + } + defer f.Close() + content = f + } else { + content = tt.content } - defer f.Close() servec <- serveParam{ name: filepath.Base(tt.file), - content: f, + content: content, modtime: tt.modtime, etag: tt.serveETag, contentType: tt.serveContentType, @@ -768,3 +801,5 @@ func TestLinuxSendfileChild(*testing.T) { panic(err) } } + +type panicOnSeek struct{ io.ReadSeeker } diff --git a/libgo/go/net/http/header.go b/libgo/go/net/http/header.go index 6374237..ca1ae07 100644 --- a/libgo/go/net/http/header.go +++ b/libgo/go/net/http/header.go @@ -173,7 +173,7 @@ func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error { // canonical key for "accept-encoding" is "Accept-Encoding". func CanonicalHeaderKey(s string) string { return textproto.CanonicalMIMEHeaderKey(s) } -// hasToken returns whether token appears with v, ASCII +// hasToken reports whether token appears with v, ASCII // case-insensitive, with space or comma boundaries. // token must be all lowercase. // v may contain mixed cased. diff --git a/libgo/go/net/http/header_test.go b/libgo/go/net/http/header_test.go index 584f100..2c896c5 100644 --- a/libgo/go/net/http/header_test.go +++ b/libgo/go/net/http/header_test.go @@ -193,6 +193,9 @@ func BenchmarkHeaderWriteSubset(b *testing.B) { } func TestHeaderWriteSubsetMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } t.Skip("Skipping alloc count test on gccgo") if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") @@ -202,6 +205,6 @@ func TestHeaderWriteSubsetMallocs(t *testing.T) { testHeader.WriteSubset(&buf, nil) }) if n > 0 { - t.Errorf("mallocs = %d; want 0", n) + t.Errorf("mallocs = %g; want 0", n) } } diff --git a/libgo/go/net/http/request.go b/libgo/go/net/http/request.go index 6d45691..57b5d09 100644 --- a/libgo/go/net/http/request.go +++ b/libgo/go/net/http/request.go @@ -10,7 +10,6 @@ import ( "bufio" "bytes" "crypto/tls" - "encoding/base64" "errors" "fmt" "io" @@ -106,7 +105,16 @@ type Request struct { // following a hyphen uppercase and the rest lowercase. Header Header - // The message body. + // Body is the request's body. + // + // 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 + // but will return EOF immediately when no body is present. + // The Server will close the request body. The ServeHTTP + // Handler does not need to. Body io.ReadCloser // ContentLength records the length of the associated content. @@ -183,7 +191,7 @@ type Request struct { TLS *tls.ConnectionState } -// ProtoAtLeast returns whether the HTTP protocol used +// ProtoAtLeast reports whether the HTTP protocol used // in the request is at least major.minor. func (r *Request) ProtoAtLeast(major, minor int) bool { return r.ProtoMajor > major || @@ -216,7 +224,7 @@ func (r *Request) Cookie(name string) (*Cookie, error) { // means all cookies, if any, are written into the same line, // separated by semicolon. func (r *Request) AddCookie(c *Cookie) { - s := fmt.Sprintf("%s=%s", sanitizeName(c.Name), sanitizeValue(c.Value)) + s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value)) if c := r.Header.Get("Cookie"); c != "" { r.Header.Set("Cookie", c+"; "+s) } else { @@ -283,6 +291,11 @@ func valueOrDefault(value, def string) string { return def } +// NOTE: This is not intended to reflect the actual Go version being used. +// It was changed from "Go http package" to "Go 1.1 package http" at the +// time of the Go 1.1 release because the former User-Agent had ended up +// on a blacklist for some intrusion detection systems. +// See https://codereview.appspot.com/7532043. const defaultUserAgent = "Go 1.1 package http" // Write writes an HTTP/1.1 request -- header and body -- in wire format. @@ -424,6 +437,10 @@ func ParseHTTPVersion(vers string) (major, minor int, ok bool) { } // NewRequest returns a new Request given a method, URL, and optional body. +// +// If the provided body is also an io.Closer, the returned +// Request.Body is set to body and will be closed by the Client +// methods Do, Post, and PostForm, and Transport.RoundTrip. func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { u, err := url.Parse(urlStr) if err != nil { @@ -463,8 +480,7 @@ func NewRequest(method, urlStr string, body io.Reader) (*Request, error) { // With HTTP Basic Authentication the provided username and password // are not encrypted. func (r *Request) SetBasicAuth(username, password string) { - s := username + ":" + password - r.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(s))) + r.Header.Set("Authorization", "Basic "+basicAuth(username, password)) } // parseRequestLine parses "GET /foo HTTP/1.1" into its three parts. diff --git a/libgo/go/net/http/request_test.go b/libgo/go/net/http/request_test.go index 692485c..89303c3 100644 --- a/libgo/go/net/http/request_test.go +++ b/libgo/go/net/http/request_test.go @@ -332,7 +332,7 @@ func TestRequestWriteBufferedWriter(t *testing.T) { func testMissingFile(t *testing.T, req *Request) { f, fh, err := req.FormFile("missing") if f != nil { - t.Errorf("FormFile file = %q, want nil", f) + t.Errorf("FormFile file = %v, want nil", f) } if fh != nil { t.Errorf("FormFile file header = %q, want nil", fh) diff --git a/libgo/go/net/http/response.go b/libgo/go/net/http/response.go index 9a7e4e3..35d0ba3 100644 --- a/libgo/go/net/http/response.go +++ b/libgo/go/net/http/response.go @@ -32,7 +32,7 @@ type Response struct { ProtoMinor int // e.g. 0 // Header maps header keys to values. If the response had multiple - // headers with the same key, they will be concatenated, with comma + // headers with the same key, they may be concatenated, with comma // delimiters. (Section 4.2 of RFC 2616 requires that multiple headers // be semantically equivalent to a comma-delimited sequence.) Values // duplicated by other fields in this struct (e.g., ContentLength) are @@ -98,18 +98,17 @@ func (r *Response) Location() (*url.URL, error) { return url.Parse(lv) } -// ReadResponse reads and returns an HTTP response from r. The -// req parameter specifies the Request that corresponds to -// this Response. Clients must call resp.Body.Close when finished -// reading resp.Body. After that call, clients can inspect -// resp.Trailer to find key/value pairs included in the response -// trailer. -func ReadResponse(r *bufio.Reader, req *Request) (resp *Response, err error) { - +// ReadResponse reads and returns an HTTP response from r. +// The req parameter optionally specifies the Request that corresponds +// to this Response. If nil, a GET request is assumed. +// Clients must call resp.Body.Close when finished reading resp.Body. +// After that call, clients can inspect resp.Trailer to find key/value +// pairs included in the response trailer. +func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) { tp := textproto.NewReader(r) - resp = new(Response) - - resp.Request = req + resp := &Response{ + Request: req, + } // Parse the first line of the response. line, err := tp.ReadLine() @@ -168,7 +167,7 @@ func fixPragmaCacheControl(header Header) { } } -// ProtoAtLeast returns whether the HTTP protocol used +// ProtoAtLeast reports whether the HTTP protocol used // in the response is at least major.minor. func (r *Response) ProtoAtLeast(major, minor int) bool { return r.ProtoMajor > major || diff --git a/libgo/go/net/http/response_test.go b/libgo/go/net/http/response_test.go index 02796e88..5044306 100644 --- a/libgo/go/net/http/response_test.go +++ b/libgo/go/net/http/response_test.go @@ -348,6 +348,29 @@ some body`, "some body", }, + + // Unchunked response without Content-Length, Request is nil + { + "HTTP/1.0 200 OK\r\n" + + "Connection: close\r\n" + + "\r\n" + + "Body here\n", + + Response{ + Status: "200 OK", + StatusCode: 200, + Proto: "HTTP/1.0", + ProtoMajor: 1, + ProtoMinor: 0, + Header: Header{ + "Connection": {"close"}, // TODO(rsc): Delete? + }, + Close: true, + ContentLength: -1, + }, + + "Body here\n", + }, } func TestReadResponse(t *testing.T) { @@ -565,3 +588,42 @@ func TestResponseStatusStutter(t *testing.T) { t.Errorf("stutter in status: %s", buf.String()) } } + +func TestResponseContentLengthShortBody(t *testing.T) { + const shortBody = "Short body, not 123 bytes." + br := bufio.NewReader(strings.NewReader("HTTP/1.1 200 OK\r\n" + + "Content-Length: 123\r\n" + + "\r\n" + + shortBody)) + res, err := ReadResponse(br, &Request{Method: "GET"}) + if err != nil { + t.Fatal(err) + } + if res.ContentLength != 123 { + t.Fatalf("Content-Length = %d; want 123", res.ContentLength) + } + var buf bytes.Buffer + n, err := io.Copy(&buf, res.Body) + if n != int64(len(shortBody)) { + t.Errorf("Copied %d bytes; want %d, len(%q)", n, len(shortBody), shortBody) + } + if buf.String() != shortBody { + t.Errorf("Read body %q; want %q", buf.String(), shortBody) + } + if err != io.ErrUnexpectedEOF { + t.Errorf("io.Copy error = %#v; want io.ErrUnexpectedEOF", err) + } +} + +func TestNeedsSniff(t *testing.T) { + // needsSniff returns true with an empty response. + r := &response{} + if got, want := r.needsSniff(), true; got != want { + t.Errorf("needsSniff = %t; want %t", got, want) + } + // needsSniff returns false when Content-Type = nil. + r.handlerHeader = Header{"Content-Type": nil} + if got, want := r.needsSniff(), false; got != want { + t.Errorf("needsSniff empty Content-Type = %t; want %t", got, want) + } +} diff --git a/libgo/go/net/http/serve_test.go b/libgo/go/net/http/serve_test.go index d7b3215..8961cf4 100644 --- a/libgo/go/net/http/serve_test.go +++ b/libgo/go/net/http/serve_test.go @@ -116,6 +116,34 @@ func (c *testConn) Close() error { return nil } +// reqBytes treats req as a request (with \n delimiters) and returns it with \r\n delimiters, +// ending in \r\n\r\n +func reqBytes(req string) []byte { + return []byte(strings.Replace(strings.TrimSpace(req), "\n", "\r\n", -1) + "\r\n\r\n") +} + +type handlerTest struct { + handler Handler +} + +func newHandlerTest(h Handler) handlerTest { + return handlerTest{h} +} + +func (ht handlerTest) rawResponse(req string) string { + reqb := reqBytes(req) + var output bytes.Buffer + conn := &rwTestConn{ + Reader: bytes.NewReader(reqb), + Writer: &output, + closec: make(chan bool, 1), + } + ln := &oneConnListener{conn: conn} + go Serve(ln, ht.handler) + <-conn.closec + return output.String() +} + func TestConsumingBodyOnNextConn(t *testing.T) { conn := new(testConn) for i := 0; i < 2; i++ { @@ -241,6 +269,152 @@ func TestHostHandlers(t *testing.T) { } } +var serveMuxRegister = []struct { + pattern string + h Handler +}{ + {"/dir/", serve(200)}, + {"/search", serve(201)}, + {"codesearch.google.com/search", serve(202)}, + {"codesearch.google.com/", serve(203)}, + {"example.com/", HandlerFunc(checkQueryStringHandler)}, +} + +// serve returns a handler that sends a response with the given code. +func serve(code int) HandlerFunc { + return func(w ResponseWriter, r *Request) { + w.WriteHeader(code) + } +} + +// checkQueryStringHandler checks if r.URL.RawQuery has the same value +// as the URL excluding the scheme and the query string and sends 200 +// response code if it is, 500 otherwise. +func checkQueryStringHandler(w ResponseWriter, r *Request) { + u := *r.URL + u.Scheme = "http" + u.Host = r.Host + u.RawQuery = "" + if "http://"+r.URL.RawQuery == u.String() { + w.WriteHeader(200) + } else { + w.WriteHeader(500) + } +} + +var serveMuxTests = []struct { + method string + host string + path string + code int + pattern string +}{ + {"GET", "google.com", "/", 404, ""}, + {"GET", "google.com", "/dir", 301, "/dir/"}, + {"GET", "google.com", "/dir/", 200, "/dir/"}, + {"GET", "google.com", "/dir/file", 200, "/dir/"}, + {"GET", "google.com", "/search", 201, "/search"}, + {"GET", "google.com", "/search/", 404, ""}, + {"GET", "google.com", "/search/foo", 404, ""}, + {"GET", "codesearch.google.com", "/search", 202, "codesearch.google.com/search"}, + {"GET", "codesearch.google.com", "/search/", 203, "codesearch.google.com/"}, + {"GET", "codesearch.google.com", "/search/foo", 203, "codesearch.google.com/"}, + {"GET", "codesearch.google.com", "/", 203, "codesearch.google.com/"}, + {"GET", "images.google.com", "/search", 201, "/search"}, + {"GET", "images.google.com", "/search/", 404, ""}, + {"GET", "images.google.com", "/search/foo", 404, ""}, + {"GET", "google.com", "/../search", 301, "/search"}, + {"GET", "google.com", "/dir/..", 301, ""}, + {"GET", "google.com", "/dir/..", 301, ""}, + {"GET", "google.com", "/dir/./file", 301, "/dir/"}, + + // The /foo -> /foo/ redirect applies to CONNECT requests + // but the path canonicalization does not. + {"CONNECT", "google.com", "/dir", 301, "/dir/"}, + {"CONNECT", "google.com", "/../search", 404, ""}, + {"CONNECT", "google.com", "/dir/..", 200, "/dir/"}, + {"CONNECT", "google.com", "/dir/..", 200, "/dir/"}, + {"CONNECT", "google.com", "/dir/./file", 200, "/dir/"}, +} + +func TestServeMuxHandler(t *testing.T) { + mux := NewServeMux() + for _, e := range serveMuxRegister { + mux.Handle(e.pattern, e.h) + } + + for _, tt := range serveMuxTests { + r := &Request{ + Method: tt.method, + Host: tt.host, + URL: &url.URL{ + Path: tt.path, + }, + } + h, pattern := mux.Handler(r) + rr := httptest.NewRecorder() + h.ServeHTTP(rr, r) + if pattern != tt.pattern || rr.Code != tt.code { + t.Errorf("%s %s %s = %d, %q, want %d, %q", tt.method, tt.host, tt.path, rr.Code, pattern, tt.code, tt.pattern) + } + } +} + +var serveMuxTests2 = []struct { + method string + host string + url string + code int + redirOk bool +}{ + {"GET", "google.com", "/", 404, false}, + {"GET", "example.com", "/test/?example.com/test/", 200, false}, + {"GET", "example.com", "test/?example.com/test/", 200, true}, +} + +// TestServeMuxHandlerRedirects tests that automatic redirects generated by +// mux.Handler() shouldn't clear the request's query string. +func TestServeMuxHandlerRedirects(t *testing.T) { + mux := NewServeMux() + for _, e := range serveMuxRegister { + mux.Handle(e.pattern, e.h) + } + + for _, tt := range serveMuxTests2 { + tries := 1 + turl := tt.url + for tries > 0 { + u, e := url.Parse(turl) + if e != nil { + t.Fatal(e) + } + r := &Request{ + Method: tt.method, + Host: tt.host, + URL: u, + } + h, _ := mux.Handler(r) + rr := httptest.NewRecorder() + h.ServeHTTP(rr, r) + if rr.Code != 301 { + if rr.Code != tt.code { + t.Errorf("%s %s %s = %d, want %d", tt.method, tt.host, tt.url, rr.Code, tt.code) + } + break + } + if !tt.redirOk { + t.Errorf("%s %s %s, unexpected redirect", tt.method, tt.host, tt.url) + break + } + turl = rr.HeaderMap.Get("Location") + tries-- + } + if tries < 0 { + t.Errorf("%s %s %s, too many redirects", tt.method, tt.host, tt.url) + } + } +} + // Tests for http://code.google.com/p/go/issues/detail?id=900 func TestMuxRedirectLeadingSlashes(t *testing.T) { paths := []string{"//foo.txt", "///foo.txt", "/../../foo.txt"} @@ -626,22 +800,20 @@ func Test304Responses(t *testing.T) { } } -// TestHeadResponses verifies that responses to HEAD requests don't -// declare that they're chunking in their response headers, aren't -// allowed to produce output, and don't set a Content-Type since -// the real type of the body data cannot be inferred. +// TestHeadResponses verifies that all MIME type sniffing and Content-Length +// counting of GET requests also happens on HEAD requests. func TestHeadResponses(t *testing.T) { defer afterTest(t) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { - _, err := w.Write([]byte("Ignored body")) - if err != ErrBodyNotAllowed { - t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err) + _, err := w.Write([]byte("<html>")) + if err != nil { + t.Errorf("ResponseWriter.Write: %v", err) } // Also exercise the ReaderFrom path - _, err = io.Copy(w, strings.NewReader("Ignored body")) - if err != ErrBodyNotAllowed { - t.Errorf("on Copy, expected ErrBodyNotAllowed, got %v", err) + _, err = io.Copy(w, strings.NewReader("789a")) + if err != nil { + t.Errorf("Copy(ResponseWriter, ...): %v", err) } })) defer ts.Close() @@ -652,9 +824,11 @@ func TestHeadResponses(t *testing.T) { if len(res.TransferEncoding) > 0 { t.Errorf("expected no TransferEncoding; got %v", res.TransferEncoding) } - ct := res.Header.Get("Content-Type") - if ct != "" { - t.Errorf("expected no Content-Type; got %s", ct) + if ct := res.Header.Get("Content-Type"); ct != "text/html; charset=utf-8" { + t.Errorf("Content-Type: %q; want text/html; charset=utf-8", ct) + } + if v := res.ContentLength; v != 10 { + t.Errorf("Content-Length: %d; want 10", v) } body, err := ioutil.ReadAll(res.Body) if err != nil { @@ -975,6 +1149,23 @@ func TestRedirectMunging(t *testing.T) { } } +func TestRedirectBadPath(t *testing.T) { + // This used to crash. It's not valid input (bad path), but it + // shouldn't crash. + rr := httptest.NewRecorder() + req := &Request{ + Method: "GET", + URL: &url.URL{ + Scheme: "http", + Path: "not-empty-but-no-leading-slash", // bogus + }, + } + Redirect(rr, req, "", 304) + if rr.Code != 304 { + t.Errorf("Code = %d; want 304", rr.Code) + } +} + // TestZeroLengthPostAndResponse exercises an optimization done by the Transport: // when there is no body (either because the method doesn't permit a body, or an // explicit Content-Length of zero is present), then the transport can re-use the @@ -1408,10 +1599,7 @@ For: func TestCloseNotifierChanLeak(t *testing.T) { defer afterTest(t) - req := []byte(strings.Replace(`GET / HTTP/1.0 -Host: golang.org - -`, "\n", "\r\n", -1)) + req := reqBytes("GET / HTTP/1.0\nHost: golang.org") for i := 0; i < 20; i++ { var output bytes.Buffer conn := &rwTestConn{ @@ -1493,11 +1681,6 @@ func TestOptions(t *testing.T) { // ones, even if the handler modifies them (~erroneously) after the // first Write. func TestHeaderToWire(t *testing.T) { - req := []byte(strings.Replace(`GET / HTTP/1.1 -Host: golang.org - -`, "\n", "\r\n", -1)) - tests := []struct { name string handler func(ResponseWriter, *Request) @@ -1660,17 +1843,10 @@ Host: golang.org }, } for _, tc := range tests { - var output bytes.Buffer - conn := &rwTestConn{ - Reader: bytes.NewReader(req), - Writer: &output, - closec: make(chan bool, 1), - } - ln := &oneConnListener{conn: conn} - go Serve(ln, HandlerFunc(tc.handler)) - <-conn.closec - if err := tc.check(output.String()); err != nil { - t.Errorf("%s: %v\nGot response:\n%s", tc.name, err, output.Bytes()) + ht := newHandlerTest(HandlerFunc(tc.handler)) + got := ht.rawResponse("GET / HTTP/1.1\nHost: golang.org") + if err := tc.check(got); err != nil { + t.Errorf("%s: %v\nGot response:\n%s", tc.name, err, got) } } } @@ -1726,7 +1902,200 @@ func TestAcceptMaxFds(t *testing.T) { } } +func TestWriteAfterHijack(t *testing.T) { + req := reqBytes("GET / HTTP/1.1\nHost: golang.org") + var buf bytes.Buffer + wrotec := make(chan bool, 1) + conn := &rwTestConn{ + Reader: bytes.NewReader(req), + Writer: &buf, + closec: make(chan bool, 1), + } + handler := HandlerFunc(func(rw ResponseWriter, r *Request) { + conn, bufrw, err := rw.(Hijacker).Hijack() + if err != nil { + t.Error(err) + return + } + go func() { + bufrw.Write([]byte("[hijack-to-bufw]")) + bufrw.Flush() + conn.Write([]byte("[hijack-to-conn]")) + conn.Close() + wrotec <- true + }() + }) + ln := &oneConnListener{conn: conn} + go Serve(ln, handler) + <-conn.closec + <-wrotec + if g, w := buf.String(), "[hijack-to-bufw][hijack-to-conn]"; g != w { + t.Errorf("wrote %q; want %q", g, w) + } +} + +// http://code.google.com/p/go/issues/detail?id=5955 +// Note that this does not test the "request too large" +// exit path from the http server. This is intentional; +// not sending Connection: close is just a minor wire +// optimization and is pointless if dealing with a +// badly behaved client. +func TestHTTP10ConnectionHeader(t *testing.T) { + defer afterTest(t) + + mux := NewServeMux() + mux.Handle("/", HandlerFunc(func(resp ResponseWriter, req *Request) {})) + ts := httptest.NewServer(mux) + defer ts.Close() + + // net/http uses HTTP/1.1 for requests, so write requests manually + tests := []struct { + req string // raw http request + expect []string // expected Connection header(s) + }{ + { + req: "GET / HTTP/1.0\r\n\r\n", + expect: nil, + }, + { + req: "OPTIONS * HTTP/1.0\r\n\r\n", + expect: nil, + }, + { + req: "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n", + expect: []string{"keep-alive"}, + }, + } + + for _, tt := range tests { + conn, err := net.Dial("tcp", ts.Listener.Addr().String()) + if err != nil { + t.Fatal("dial err:", err) + } + + _, err = fmt.Fprint(conn, tt.req) + if err != nil { + t.Fatal("conn write err:", err) + } + + resp, err := ReadResponse(bufio.NewReader(conn), &Request{Method: "GET"}) + if err != nil { + t.Fatal("ReadResponse err:", err) + } + conn.Close() + resp.Body.Close() + + got := resp.Header["Connection"] + if !reflect.DeepEqual(got, tt.expect) { + t.Errorf("wrong Connection headers for request %q. Got %q expect %q", tt.req, got, tt.expect) + } + } +} + +// See golang.org/issue/5660 +func TestServerReaderFromOrder(t *testing.T) { + defer afterTest(t) + pr, pw := io.Pipe() + const size = 3 << 20 + ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + rw.Header().Set("Content-Type", "text/plain") // prevent sniffing path + done := make(chan bool) + go func() { + io.Copy(rw, pr) + close(done) + }() + time.Sleep(25 * time.Millisecond) // give Copy a chance to break things + n, err := io.Copy(ioutil.Discard, req.Body) + if err != nil { + t.Errorf("handler Copy: %v", err) + return + } + if n != size { + t.Errorf("handler Copy = %d; want %d", n, size) + } + pw.Write([]byte("hi")) + pw.Close() + <-done + })) + defer ts.Close() + + req, err := NewRequest("POST", ts.URL, io.LimitReader(neverEnding('a'), size)) + if err != nil { + t.Fatal(err) + } + res, err := DefaultClient.Do(req) + if err != nil { + t.Fatal(err) + } + all, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + if string(all) != "hi" { + t.Errorf("Body = %q; want hi", all) + } +} + +// Issue 6157 +func TestNoContentTypeOnNotModified(t *testing.T) { + ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) { + if r.URL.Path == "/header" { + w.Header().Set("Content-Length", "123") + } + w.WriteHeader(StatusNotModified) + if r.URL.Path == "/more" { + w.Write([]byte("stuff")) + } + })) + for _, req := range []string{ + "GET / HTTP/1.0", + "GET /header HTTP/1.0", + "GET /more HTTP/1.0", + "GET / HTTP/1.1", + "GET /header HTTP/1.1", + "GET /more HTTP/1.1", + } { + got := ht.rawResponse(req) + if !strings.Contains(got, "304 Not Modified") { + t.Errorf("Non-304 Not Modified for %q: %s", req, got) + } else if strings.Contains(got, "Content-Length") { + t.Errorf("Got a Content-Length from %q: %s", req, got) + } + } +} + +func TestResponseWriterWriteStringAllocs(t *testing.T) { + t.Skip("allocs test unreliable with gccgo") + ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) { + if r.URL.Path == "/s" { + io.WriteString(w, "Hello world") + } else { + 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") }) + if int(after) >= int(before) { + t.Errorf("WriteString allocs of %v >= Write allocs of %v", after, before) + } +} + +func TestAppendTime(t *testing.T) { + var b [len(TimeFormat)]byte + t1 := time.Date(2013, 9, 21, 15, 41, 0, 0, time.FixedZone("CEST", 2*60*60)) + res := ExportAppendTime(b[:0], t1) + t2, err := ParseTime(string(res)) + if err != nil { + t.Fatalf("Error parsing time: %s", err) + } + if !t1.Equal(t2) { + t.Fatalf("Times differ; expected: %v, got %v (%s)", t1, t2, string(res)) + } +} + func BenchmarkClientServer(b *testing.B) { + b.ReportAllocs() b.StopTimer() ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { fmt.Fprintf(rw, "Hello world.\n") @@ -1761,6 +2130,7 @@ func BenchmarkClientServerParallel64(b *testing.B) { } func benchmarkClientServerParallel(b *testing.B, conc int) { + b.ReportAllocs() b.StopTimer() ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { fmt.Fprintf(rw, "Hello world.\n") @@ -1805,6 +2175,7 @@ func benchmarkClientServerParallel(b *testing.B, conc int) { // $ go tool pprof http.test http.prof // (pprof) web func BenchmarkServer(b *testing.B) { + b.ReportAllocs() // Child process mode; if url := os.Getenv("TEST_BENCH_SERVER_URL"); url != "" { n, err := strconv.Atoi(os.Getenv("TEST_BENCH_CLIENT_N")) @@ -1851,15 +2222,14 @@ func BenchmarkServer(b *testing.B) { func BenchmarkServerFakeConnNoKeepAlive(b *testing.B) { b.ReportAllocs() - req := []byte(strings.Replace(`GET / HTTP/1.0 + req := reqBytes(`GET / HTTP/1.0 Host: golang.org Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 - -`, "\n", "\r\n", -1)) +`) res := []byte("Hello world!\n") conn := &testConn{ @@ -1905,15 +2275,14 @@ func (r *repeatReader) Read(p []byte) (n int, err error) { func BenchmarkServerFakeConnWithKeepAlive(b *testing.B) { b.ReportAllocs() - req := []byte(strings.Replace(`GET / HTTP/1.1 + req := reqBytes(`GET / HTTP/1.1 Host: golang.org Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.52 Safari/537.17 Accept-Encoding: gzip,deflate,sdch Accept-Language: en-US,en;q=0.8 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 - -`, "\n", "\r\n", -1)) +`) res := []byte("Hello world!\n") conn := &rwTestConn{ @@ -1940,10 +2309,9 @@ Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3 func BenchmarkServerFakeConnWithKeepAliveLite(b *testing.B) { b.ReportAllocs() - req := []byte(strings.Replace(`GET / HTTP/1.1 + req := reqBytes(`GET / HTTP/1.1 Host: golang.org - -`, "\n", "\r\n", -1)) +`) res := []byte("Hello world!\n") conn := &rwTestConn{ @@ -2003,10 +2371,9 @@ func BenchmarkServerHandlerNoHeader(b *testing.B) { func benchmarkHandler(b *testing.B, h Handler) { b.ReportAllocs() - req := []byte(strings.Replace(`GET / HTTP/1.1 + req := reqBytes(`GET / HTTP/1.1 Host: golang.org - -`, "\n", "\r\n", -1)) +`) conn := &rwTestConn{ Reader: &repeatReader{content: req, count: b.N}, Writer: ioutil.Discard, diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index b259607..0e46863 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -16,6 +16,7 @@ import ( "log" "net" "net/url" + "os" "path" "runtime" "strconv" @@ -109,8 +110,6 @@ type conn struct { sr liveSwitchReader // where the LimitReader reads from; usually the rwc lr *io.LimitedReader // io.LimitReader(sr) buf *bufio.ReadWriter // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc - bufswr *switchReader // the *switchReader io.Reader source of buf - bufsww *switchWriter // the *switchWriter io.Writer dest of buf tlsState *tls.ConnectionState // or nil when not using TLS mu sync.Mutex // guards the following @@ -246,6 +245,10 @@ func (cw *chunkWriter) Write(p []byte) (n int, err error) { if !cw.wroteHeader { cw.writeHeader(p) } + if cw.res.req.Method == "HEAD" { + // Eat writes. + return len(p), nil + } if cw.chunking { _, err = fmt.Fprintf(cw.res.conn.buf, "%x\r\n", len(p)) if err != nil { @@ -278,7 +281,7 @@ func (cw *chunkWriter) close() { // zero EOF chunk, trailer key/value pairs (currently // unsupported in Go's server), followed by a blank // line. - io.WriteString(cw.res.conn.buf, "0\r\n\r\n") + cw.res.conn.buf.WriteString("0\r\n\r\n") } } @@ -320,6 +323,10 @@ type response struct { requestBodyLimitHit bool handlerDone bool // set true when the handler exits + + // Buffers for Date and Content-Length + dateBuf [len(TimeFormat)]byte + clenBuf [10]byte } // requestTooLarge is called by maxBytesReader when too much input has @@ -332,16 +339,50 @@ func (w *response) requestTooLarge() { } } -// needsSniff returns whether a Content-Type still needs to be sniffed. +// needsSniff reports whether a Content-Type still needs to be sniffed. func (w *response) needsSniff() bool { - return !w.cw.wroteHeader && w.handlerHeader.Get("Content-Type") == "" && w.written < sniffLen + _, haveType := w.handlerHeader["Content-Type"] + return !w.cw.wroteHeader && !haveType && w.written < sniffLen } +// writerOnly hides an io.Writer value's optional ReadFrom method +// from io.Copy. type writerOnly struct { io.Writer } +func srcIsRegularFile(src io.Reader) (isRegular bool, err error) { + switch v := src.(type) { + case *os.File: + fi, err := v.Stat() + if err != nil { + return false, err + } + return fi.Mode().IsRegular(), nil + case *io.LimitedReader: + return srcIsRegularFile(v.R) + default: + return + } +} + +// ReadFrom is here to optimize copying from an *os.File regular file +// to a *net.TCPConn with sendfile. func (w *response) ReadFrom(src io.Reader) (n int64, err error) { + // Our underlying w.conn.rwc is usually a *TCPConn (with its + // own ReadFrom method). If not, or if our src isn't a regular + // file, just fall back to the normal copy method. + rf, ok := w.conn.rwc.(io.ReaderFrom) + regFile, err := srcIsRegularFile(src) + if err != nil { + return 0, err + } + if !ok || !regFile { + return io.Copy(writerOnly{w}, src) + } + + // sendfile path: + if !w.wroteHeader { w.WriteHeader(StatusOK) } @@ -359,16 +400,12 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) { // Now that cw has been flushed, its chunking field is guaranteed initialized. if !w.cw.chunking && w.bodyAllowed() { - if rf, ok := w.conn.rwc.(io.ReaderFrom); ok { - n0, err := rf.ReadFrom(src) - n += n0 - w.written += n0 - return n, err - } + n0, err := rf.ReadFrom(src) + n += n0 + w.written += n0 + return n, err } - // Fall back to default io.Copy implementation. - // Use wrapper to hide w.ReadFrom from io.Copy. n0, err := io.Copy(writerOnly{w}, src) n += n0 return n, err @@ -392,34 +429,20 @@ func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) { } c.sr = liveSwitchReader{r: c.rwc} c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader) - br, sr := newBufioReader(c.lr) - bw, sw := newBufioWriterSize(c.rwc, 4<<10) + br := newBufioReader(c.lr) + bw := newBufioWriterSize(c.rwc, 4<<10) c.buf = bufio.NewReadWriter(br, bw) - c.bufswr = sr - c.bufsww = sw return c, nil } -// TODO: remove this, if issue 5100 is fixed -type bufioReaderPair struct { - br *bufio.Reader - sr *switchReader // from which the bufio.Reader is reading -} - -// TODO: remove this, if issue 5100 is fixed -type bufioWriterPair struct { - bw *bufio.Writer - sw *switchWriter // to which the bufio.Writer is writing -} - // TODO: use a sync.Cache instead var ( - bufioReaderCache = make(chan bufioReaderPair, 4) - bufioWriterCache2k = make(chan bufioWriterPair, 4) - bufioWriterCache4k = make(chan bufioWriterPair, 4) + bufioReaderCache = make(chan *bufio.Reader, 4) + bufioWriterCache2k = make(chan *bufio.Writer, 4) + bufioWriterCache4k = make(chan *bufio.Writer, 4) ) -func bufioWriterCache(size int) chan bufioWriterPair { +func bufioWriterCache(size int) chan *bufio.Writer { switch size { case 2 << 10: return bufioWriterCache2k @@ -429,55 +452,38 @@ func bufioWriterCache(size int) chan bufioWriterPair { return nil } -func newBufioReader(r io.Reader) (*bufio.Reader, *switchReader) { +func newBufioReader(r io.Reader) *bufio.Reader { select { case p := <-bufioReaderCache: - p.sr.Reader = r - return p.br, p.sr + p.Reset(r) + return p default: - sr := &switchReader{r} - return bufio.NewReader(sr), sr + return bufio.NewReader(r) } } -func putBufioReader(br *bufio.Reader, sr *switchReader) { - if n := br.Buffered(); n > 0 { - io.CopyN(ioutil.Discard, br, int64(n)) - } - br.Read(nil) // clears br.err - sr.Reader = nil +func putBufioReader(br *bufio.Reader) { + br.Reset(nil) select { - case bufioReaderCache <- bufioReaderPair{br, sr}: + case bufioReaderCache <- br: default: } } -func newBufioWriterSize(w io.Writer, size int) (*bufio.Writer, *switchWriter) { +func newBufioWriterSize(w io.Writer, size int) *bufio.Writer { select { case p := <-bufioWriterCache(size): - p.sw.Writer = w - return p.bw, p.sw + p.Reset(w) + return p default: - sw := &switchWriter{w} - return bufio.NewWriterSize(sw, size), sw + return bufio.NewWriterSize(w, size) } } -func putBufioWriter(bw *bufio.Writer, sw *switchWriter) { - if bw.Buffered() > 0 { - // It must have failed to flush to its target - // earlier. We can't reuse this bufio.Writer. - return - } - if err := bw.Flush(); err != nil { - // Its sticky error field is set, which is returned by - // Flush even when there's no data buffered. This - // bufio Writer is dead to us. Don't reuse it. - return - } - sw.Writer = nil +func putBufioWriter(bw *bufio.Writer) { + bw.Reset(nil) select { - case bufioWriterCache(bw.Available()) <- bufioWriterPair{bw, sw}: + case bufioWriterCache(bw.Available()) <- bw: default: } } @@ -508,7 +514,7 @@ func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { } if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() { ecr.resp.wroteContinue = true - io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n") + ecr.resp.conn.buf.WriteString("HTTP/1.1 100 Continue\r\n\r\n") ecr.resp.conn.buf.Flush() } return ecr.readCloser.Read(p) @@ -525,6 +531,28 @@ func (ecr *expectContinueReader) Close() error { // It is like time.RFC1123 but hard codes GMT as the time zone. const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" +// appendTime is a non-allocating version of []byte(t.UTC().Format(TimeFormat)) +func appendTime(b []byte, t time.Time) []byte { + const days = "SunMonTueWedThuFriSat" + const months = "JanFebMarAprMayJunJulAugSepOctNovDec" + + t = t.UTC() + yy, mm, dd := t.Date() + hh, mn, ss := t.Clock() + day := days[3*t.Weekday():] + mon := months[3*(mm-1):] + + return append(b, + day[0], day[1], day[2], ',', ' ', + byte('0'+dd/10), byte('0'+dd%10), ' ', + mon[0], mon[1], mon[2], ' ', + byte('0'+yy/1000), byte('0'+(yy/100)%10), byte('0'+(yy/10)%10), byte('0'+yy%10), ' ', + byte('0'+hh/10), byte('0'+hh%10), ':', + byte('0'+mn/10), byte('0'+mn%10), ':', + byte('0'+ss/10), byte('0'+ss%10), ' ', + 'G', 'M', 'T') +} + var errTooLarge = errors.New("http: request too large") // Read next request from connection. @@ -562,7 +590,7 @@ func (c *conn) readRequest() (w *response, err error) { contentLength: -1, } w.cw.res = w - w.w, w.sw = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize) + w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize) return w, nil } @@ -620,27 +648,45 @@ func (w *response) WriteHeader(code int) { // the response Header map and all its 1-element slices. type extraHeader struct { contentType string - contentLength string connection string - date string transferEncoding string + date []byte // written if not nil + contentLength []byte // written if not nil } // Sorted the same as extraHeader.Write's loop. var extraHeaderKeys = [][]byte{ - []byte("Content-Type"), []byte("Content-Length"), - []byte("Connection"), []byte("Date"), []byte("Transfer-Encoding"), + []byte("Content-Type"), + []byte("Connection"), + []byte("Transfer-Encoding"), } -// The value receiver, despite copying 5 strings to the stack, -// prevents an extra allocation. The escape analysis isn't smart -// enough to realize this doesn't mutate h. -func (h extraHeader) Write(w io.Writer) { - for i, v := range []string{h.contentType, h.contentLength, h.connection, h.date, h.transferEncoding} { +var ( + headerContentLength = []byte("Content-Length: ") + headerDate = []byte("Date: ") +) + +// Write writes the headers described in h to w. +// +// This method has a value receiver, despite the somewhat large size +// of h, because it prevents an allocation. The escape analysis isn't +// smart enough to realize this function doesn't mutate h. +func (h extraHeader) Write(w *bufio.Writer) { + if h.date != nil { + w.Write(headerDate) + w.Write(h.date) + w.Write(crlf) + } + if h.contentLength != nil { + w.Write(headerContentLength) + w.Write(h.contentLength) + w.Write(crlf) + } + for i, v := range []string{h.contentType, h.connection, h.transferEncoding} { if v != "" { w.Write(extraHeaderKeys[i]) w.Write(colonSpace) - io.WriteString(w, v) + w.WriteString(v) w.Write(crlf) } } @@ -661,6 +707,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { cw.wroteHeader = true w := cw.res + isHEAD := w.req.Method == "HEAD" // header is written out to w.conn.buf below. Depending on the // state of the handler, we either own the map or not. If we @@ -692,9 +739,17 @@ func (cw *chunkWriter) writeHeader(p []byte) { // response header and this is our first (and last) write, set // it, even to zero. This helps HTTP/1.0 clients keep their // "keep-alive" connections alive. - if w.handlerDone && header.get("Content-Length") == "" && w.req.Method != "HEAD" { + // Exceptions: 304 responses never get Content-Length, and if + // it was a HEAD request, we don't know the difference between + // 0 actual bytes and 0 bytes because the handler noticed it + // was a HEAD request and chose not to write anything. So for + // HEAD, the handler should either write the Content-Length or + // write non-zero bytes. If it's actually 0 bytes and the + // handler never looked at the Request.Method, we just don't + // send a Content-Length header. + if w.handlerDone && w.status != StatusNotModified && header.get("Content-Length") == "" && (!isHEAD || len(p) > 0) { w.contentLength = int64(len(p)) - setHeader.contentLength = strconv.Itoa(len(p)) + setHeader.contentLength = strconv.AppendInt(cw.res.clenBuf[:0], int64(len(p)), 10) } // If this was an HTTP/1.0 request with keep-alive and we sent a @@ -709,7 +764,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // Check for a explicit (and valid) Content-Length header. hasCL := w.contentLength != -1 - if w.req.wantsHttp10KeepAlive() && (w.req.Method == "HEAD" || hasCL) { + if w.req.wantsHttp10KeepAlive() && (isHEAD || hasCL) { _, connectionHeaderSet := header["Connection"] if !connectionHeaderSet { setHeader.connection = "keep-alive" @@ -749,13 +804,14 @@ func (cw *chunkWriter) writeHeader(p []byte) { } } else { // If no content type, apply sniffing algorithm to body. - if header.get("Content-Type") == "" && w.req.Method != "HEAD" { + _, haveType := header["Content-Type"] + if !haveType { setHeader.contentType = DetectContentType(p) } } if _, ok := header["Date"]; !ok { - setHeader.date = time.Now().UTC().Format(TimeFormat) + setHeader.date = appendTime(cw.res.dateBuf[:0], time.Now()) } te := header.get("Transfer-Encoding") @@ -801,12 +857,14 @@ func (cw *chunkWriter) writeHeader(p []byte) { if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") { delHeader("Connection") - setHeader.connection = "close" + if w.req.ProtoAtLeast(1, 1) { + setHeader.connection = "close" + } } - io.WriteString(w.conn.buf, statusLine(w.req, code)) + w.conn.buf.WriteString(statusLine(w.req, code)) cw.header.WriteSubset(w.conn.buf, excludeHeader) - setHeader.Write(w.conn.buf) + setHeader.Write(w.conn.buf.Writer) w.conn.buf.Write(crlf) } @@ -861,7 +919,7 @@ func (w *response) bodyAllowed() bool { if !w.wroteHeader { panic("") } - return w.status != StatusNotModified && w.req.Method != "HEAD" + return w.status != StatusNotModified } // The Life Of A Write is like this: @@ -897,6 +955,15 @@ func (w *response) bodyAllowed() bool { // bufferBeforeChunkingSize smaller and having bufio's fast-paths deal // with this instead. func (w *response) Write(data []byte) (n int, err error) { + return w.write(len(data), data, "") +} + +func (w *response) WriteString(data string) (n int, err error) { + return w.write(len(data), nil, data) +} + +// 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") return 0, ErrHijacked @@ -904,18 +971,22 @@ func (w *response) Write(data []byte) (n int, err error) { if !w.wroteHeader { w.WriteHeader(StatusOK) } - if len(data) == 0 { + if lenData == 0 { return 0, nil } if !w.bodyAllowed() { return 0, ErrBodyNotAllowed } - w.written += int64(len(data)) // ignoring errors, for errorKludge + w.written += int64(lenData) // ignoring errors, for errorKludge if w.contentLength != -1 && w.written > w.contentLength { return 0, ErrContentLength } - return w.w.Write(data) + if dataB != nil { + return w.w.Write(dataB) + } else { + return w.w.WriteString(dataS) + } } func (w *response) finishRequest() { @@ -926,7 +997,7 @@ func (w *response) finishRequest() { } w.w.Flush() - putBufioWriter(w.w, w.sw) + putBufioWriter(w.w) w.cw.close() w.conn.buf.Flush() @@ -939,7 +1010,7 @@ func (w *response) finishRequest() { w.req.MultipartForm.RemoveAll() } - if w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written { + if w.req.Method != "HEAD" && w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written { // Did not write enough. Avoid getting out of sync. w.closeAfterReply = true } @@ -959,11 +1030,11 @@ func (c *conn) finalFlush() { // Steal the bufio.Reader (~4KB worth of memory) and its associated // reader for a future connection. - putBufioReader(c.buf.Reader, c.bufswr) + putBufioReader(c.buf.Reader) // Steal the bufio.Writer (~4KB worth of memory) and its associated // writer for a future connection. - putBufioWriter(c.buf.Writer, c.bufsww) + putBufioWriter(c.buf.Writer) c.buf = nil } @@ -1001,7 +1072,7 @@ func (c *conn) closeWriteAndWait() { time.Sleep(rstAvoidanceDelay) } -// validNPN returns whether the proto is not a blacklisted Next +// validNPN reports whether the proto is not a blacklisted Next // Protocol Negotiation protocol. Empty and built-in protocol types // are blacklisted and can't be overridden with alternate // implementations. @@ -1152,6 +1223,7 @@ func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { // Helper handlers // Error replies to the request with the specified error message and HTTP code. +// The error message should be plain text. func Error(w ResponseWriter, error string, code int) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(code) @@ -1288,6 +1360,10 @@ func RedirectHandler(url string, code int) Handler { // former will receive requests for any other paths in the // "/images/" subtree. // +// Note that since a pattern ending in a slash names a rooted subtree, +// the pattern "/" matches all paths not matched by other registered +// patterns, not just the URL with Path == "/". +// // Patterns may optionally begin with a host name, restricting matches to // URLs on that host only. Host-specific patterns take precedence over // general patterns, so that a handler might register for the two patterns @@ -1378,7 +1454,9 @@ func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { if r.Method != "CONNECT" { if p := cleanPath(r.URL.Path); p != r.URL.Path { _, pattern = mux.handler(r.Host, p) - return RedirectHandler(p, StatusMovedPermanently), pattern + url := *r.URL + url.Path = p + return RedirectHandler(url.String(), StatusMovedPermanently), pattern } } @@ -1408,7 +1486,9 @@ func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { // pattern most closely matches the request URL. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { - w.Header().Set("Connection", "close") + if r.ProtoAtLeast(1, 1) { + w.Header().Set("Connection", "close") + } w.WriteHeader(StatusBadRequest) return } @@ -1771,7 +1851,15 @@ func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) { } // eofReader is a non-nil io.ReadCloser that always returns EOF. -var eofReader = ioutil.NopCloser(strings.NewReader("")) +// It embeds a *strings.Reader so it still has a WriteTo method +// and io.Copy won't need a buffer. +var eofReader = &struct { + *strings.Reader + io.Closer +}{ + strings.NewReader(""), + ioutil.NopCloser(nil), +} // initNPNRequest is an HTTP handler that initializes certain // uninitialized fields in its *Request. Such partially-initialized diff --git a/libgo/go/net/http/server_test.go b/libgo/go/net/http/server_test.go deleted file mode 100644 index e8b69f7..0000000 --- a/libgo/go/net/http/server_test.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2012 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package http_test - -import ( - . "net/http" - "net/http/httptest" - "net/url" - "testing" -) - -var serveMuxRegister = []struct { - pattern string - h Handler -}{ - {"/dir/", serve(200)}, - {"/search", serve(201)}, - {"codesearch.google.com/search", serve(202)}, - {"codesearch.google.com/", serve(203)}, -} - -// serve returns a handler that sends a response with the given code. -func serve(code int) HandlerFunc { - return func(w ResponseWriter, r *Request) { - w.WriteHeader(code) - } -} - -var serveMuxTests = []struct { - method string - host string - path string - code int - pattern string -}{ - {"GET", "google.com", "/", 404, ""}, - {"GET", "google.com", "/dir", 301, "/dir/"}, - {"GET", "google.com", "/dir/", 200, "/dir/"}, - {"GET", "google.com", "/dir/file", 200, "/dir/"}, - {"GET", "google.com", "/search", 201, "/search"}, - {"GET", "google.com", "/search/", 404, ""}, - {"GET", "google.com", "/search/foo", 404, ""}, - {"GET", "codesearch.google.com", "/search", 202, "codesearch.google.com/search"}, - {"GET", "codesearch.google.com", "/search/", 203, "codesearch.google.com/"}, - {"GET", "codesearch.google.com", "/search/foo", 203, "codesearch.google.com/"}, - {"GET", "codesearch.google.com", "/", 203, "codesearch.google.com/"}, - {"GET", "images.google.com", "/search", 201, "/search"}, - {"GET", "images.google.com", "/search/", 404, ""}, - {"GET", "images.google.com", "/search/foo", 404, ""}, - {"GET", "google.com", "/../search", 301, "/search"}, - {"GET", "google.com", "/dir/..", 301, ""}, - {"GET", "google.com", "/dir/..", 301, ""}, - {"GET", "google.com", "/dir/./file", 301, "/dir/"}, - - // The /foo -> /foo/ redirect applies to CONNECT requests - // but the path canonicalization does not. - {"CONNECT", "google.com", "/dir", 301, "/dir/"}, - {"CONNECT", "google.com", "/../search", 404, ""}, - {"CONNECT", "google.com", "/dir/..", 200, "/dir/"}, - {"CONNECT", "google.com", "/dir/..", 200, "/dir/"}, - {"CONNECT", "google.com", "/dir/./file", 200, "/dir/"}, -} - -func TestServeMuxHandler(t *testing.T) { - mux := NewServeMux() - for _, e := range serveMuxRegister { - mux.Handle(e.pattern, e.h) - } - - for _, tt := range serveMuxTests { - r := &Request{ - Method: tt.method, - Host: tt.host, - URL: &url.URL{ - Path: tt.path, - }, - } - h, pattern := mux.Handler(r) - rr := httptest.NewRecorder() - h.ServeHTTP(rr, r) - if pattern != tt.pattern || rr.Code != tt.code { - t.Errorf("%s %s %s = %d, %q, want %d, %q", tt.method, tt.host, tt.path, rr.Code, pattern, tt.code, tt.pattern) - } - } -} - -func TestServerRedirect(t *testing.T) { - // This used to crash. It's not valid input (bad path), but it - // shouldn't crash. - rr := httptest.NewRecorder() - req := &Request{ - Method: "GET", - URL: &url.URL{ - Scheme: "http", - Path: "not-empty-but-no-leading-slash", // bogus - }, - } - Redirect(rr, req, "", 304) - if rr.Code != 304 { - t.Errorf("Code = %d; want 304", rr.Code) - } -} diff --git a/libgo/go/net/http/sniff_test.go b/libgo/go/net/http/sniff_test.go index 106d94ec..24ca27a 100644 --- a/libgo/go/net/http/sniff_test.go +++ b/libgo/go/net/http/sniff_test.go @@ -12,6 +12,7 @@ import ( "log" . "net/http" "net/http/httptest" + "reflect" "strconv" "strings" "testing" @@ -84,6 +85,29 @@ func TestServerContentType(t *testing.T) { } } +// Issue 5953: shouldn't sniff if the handler set a Content-Type header, +// even if it's the empty string. +func TestServerIssue5953(t *testing.T) { + defer afterTest(t) + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + w.Header()["Content-Type"] = []string{""} + fmt.Fprintf(w, "<html><head></head><body>hi</body></html>") + })) + defer ts.Close() + + resp, err := Get(ts.URL) + if err != nil { + t.Fatal(err) + } + + got := resp.Header["Content-Type"] + want := []string{""} + if !reflect.DeepEqual(got, want) { + t.Errorf("Content-Type = %q; want %q", got, want) + } + resp.Body.Close() +} + func TestContentTypeWithCopy(t *testing.T) { defer afterTest(t) diff --git a/libgo/go/net/http/transfer.go b/libgo/go/net/http/transfer.go index 53569bc..bacd837 100644 --- a/libgo/go/net/http/transfer.go +++ b/libgo/go/net/http/transfer.go @@ -238,7 +238,7 @@ type transferReader struct { Trailer Header } -// bodyAllowedForStatus returns whether a given response status code +// bodyAllowedForStatus reports whether a given response status code // permits a body. See RFC2616, section 4.4. func bodyAllowedForStatus(status int) bool { switch { @@ -254,7 +254,7 @@ func bodyAllowedForStatus(status int) bool { // msg is *Request or *Response. func readTransfer(msg interface{}, r *bufio.Reader) (err error) { - t := &transferReader{} + t := &transferReader{RequestMethod: "GET"} // Unify input isResponse := false @@ -262,11 +262,13 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { case *Response: t.Header = rr.Header t.StatusCode = rr.StatusCode - t.RequestMethod = rr.Request.Method t.ProtoMajor = rr.ProtoMajor t.ProtoMinor = rr.ProtoMinor t.Close = shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header) isResponse = true + if rr.Request != nil { + t.RequestMethod = rr.Request.Method + } case *Request: t.Header = rr.Header t.ProtoMajor = rr.ProtoMajor @@ -274,7 +276,6 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { // Transfer semantics for Requests are exactly like those for // Responses with status code 200, responding to a GET method t.StatusCode = 200 - t.RequestMethod = "GET" default: panic("unexpected type") } @@ -328,12 +329,12 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { switch { case chunked(t.TransferEncoding): if noBodyExpected(t.RequestMethod) { - t.Body = &body{Reader: eofReader, closing: t.Close} + t.Body = eofReader } else { t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close} } case realLength == 0: - t.Body = &body{Reader: eofReader, closing: t.Close} + t.Body = eofReader case realLength > 0: t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close} default: @@ -343,7 +344,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) { t.Body = &body{Reader: r, closing: t.Close} } else { // Persistent connection (i.e. HTTP/1.1) - t.Body = &body{Reader: eofReader, closing: t.Close} + t.Body = eofReader } } @@ -518,8 +519,6 @@ type body struct { r *bufio.Reader // underlying wire-format reader for the trailer closing bool // is the connection to be closed after reading body? closed bool - - res *response // response writer for server requests, else nil } // ErrBodyReadAfterClose is returned when reading a Request or Response @@ -534,13 +533,22 @@ func (b *body) Read(p []byte) (n int, err error) { } n, err = b.Reader.Read(p) - // Read the final trailer once we hit EOF. - if err == io.EOF && b.hdr != nil { - if e := b.readTrailer(); e != nil { - err = e + if err == io.EOF { + // Chunked case. Read the trailer. + if b.hdr != nil { + if e := b.readTrailer(); e != nil { + err = e + } + b.hdr = nil + } else { + // If the server declared the Content-Length, our body is a LimitedReader + // and we need to check whether this EOF arrived early. + if lr, ok := b.Reader.(*io.LimitedReader); ok && lr.N > 0 { + err = io.ErrUnexpectedEOF + } } - b.hdr = nil } + return n, err } @@ -618,14 +626,6 @@ func (b *body) Close() error { case b.hdr == nil && b.closing: // no trailer and closing the connection next. // no point in reading to EOF. - case b.res != nil && b.res.requestBodyLimitHit: - // In a server request, don't continue reading from the client - // if we've already hit the maximum body size set by the - // handler. If this is set, that also means the TCP connection - // is about to be closed, so getting to the next HTTP request - // in the stream is not necessary. - case b.Reader == eofReader: - // Nothing to read. No need to io.Copy from it. default: // Fully consume the body, which will also lead to us reading // the trailer headers after the body, if present. diff --git a/libgo/go/net/http/transport.go b/libgo/go/net/http/transport.go index 4cd0533..f6871af 100644 --- a/libgo/go/net/http/transport.go +++ b/libgo/go/net/http/transport.go @@ -13,7 +13,6 @@ import ( "bufio" "compress/gzip" "crypto/tls" - "encoding/base64" "errors" "fmt" "io" @@ -109,9 +108,11 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) { } proxyURL, err := url.Parse(proxy) if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") { - if u, err := url.Parse("http://" + proxy); err == nil { - proxyURL = u - err = nil + // proxy was bogus. Try prepending "http://" to it and + // see if that parses correctly. If not, we fall + // through and complain about the original one. + if proxyURL, err := url.Parse("http://" + proxy); err == nil { + return proxyURL, nil } } if err != nil { @@ -215,6 +216,7 @@ func (t *Transport) CloseIdleConnections() { t.idleMu.Lock() m := t.idleConn t.idleConn = nil + t.idleConnCh = nil t.idleMu.Unlock() if m == nil { return @@ -270,7 +272,9 @@ func (cm *connectMethod) proxyAuth() string { return "" } if u := cm.proxyURL.User; u != nil { - return "Basic " + base64.URLEncoding.EncodeToString([]byte(u.String())) + username := u.Username() + password, _ := u.Password() + return "Basic " + basicAuth(username, password) } return "" } @@ -293,8 +297,10 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool { max = DefaultMaxIdleConnsPerHost } t.idleMu.Lock() + + waitingDialer := t.idleConnCh[key] select { - case t.idleConnCh[key] <- pconn: + case waitingDialer <- pconn: // We're done with this pconn and somebody else is // currently waiting for a conn of this type (they're // actively dialing, but this conn is ready @@ -303,6 +309,11 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool { t.idleMu.Unlock() return true default: + if waitingDialer != nil { + // They had populated this, but their dial won + // first, so we can clean up this map entry. + delete(t.idleConnCh, key) + } } if t.idleConn == nil { t.idleConn = make(map[string][]*persistConn) @@ -322,7 +333,13 @@ func (t *Transport) putIdleConn(pconn *persistConn) bool { return true } +// getIdleConnCh returns a channel to receive and return idle +// persistent connection for the given connectMethod. +// It may return nil, if persistent connections are not being used. func (t *Transport) getIdleConnCh(cm *connectMethod) chan *persistConn { + if t.DisableKeepAlives { + return nil + } key := cm.key() t.idleMu.Lock() defer t.idleMu.Unlock() @@ -498,8 +515,8 @@ func (t *Transport) dialConn(cm *connectMethod) (*persistConn, error) { if err = conn.(*tls.Conn).Handshake(); err != nil { return nil, err } - if t.TLSClientConfig == nil || !t.TLSClientConfig.InsecureSkipVerify { - if err = conn.(*tls.Conn).VerifyHostname(cm.tlsHost()); err != nil { + if !cfg.InsecureSkipVerify { + if err = conn.(*tls.Conn).VerifyHostname(cfg.ServerName); err != nil { return nil, err } } @@ -831,10 +848,15 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err // uncompress the gzip stream if we were the layer that // requested it. requestedGzip := false - if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" { + if !pc.t.DisableCompression && req.Header.Get("Accept-Encoding") == "" && req.Method != "HEAD" { // Request gzip only, not deflate. Deflate is ambiguous and // not as universally supported anyway. // See: http://www.gzip.org/zlib/zlib_faq.html#faq38 + // + // Note that we don't request this for HEAD requests, + // due to a bug in nginx: + // http://trac.nginx.org/nginx/ticket/358 + // http://golang.org/issue/5522 requestedGzip = true req.extraHeaders().Set("Accept-Encoding", "gzip") } diff --git a/libgo/go/net/http/transport_test.go b/libgo/go/net/http/transport_test.go index 9f64a6e..e4df30a 100644 --- a/libgo/go/net/http/transport_test.go +++ b/libgo/go/net/http/transport_test.go @@ -15,6 +15,7 @@ import ( "io" "io/ioutil" "net" + "net/http" . "net/http" "net/http/httptest" "net/url" @@ -469,6 +470,7 @@ func TestTransportHeadResponses(t *testing.T) { res, err := c.Head(ts.URL) if err != nil { t.Errorf("error on loop %d: %v", i, err) + continue } if e, g := "123", res.Header.Get("Content-Length"); e != g { t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g) @@ -476,6 +478,11 @@ func TestTransportHeadResponses(t *testing.T) { if e, g := int64(123), res.ContentLength; e != g { t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g) } + if all, err := ioutil.ReadAll(res.Body); err != nil { + t.Errorf("loop %d: Body ReadAll: %v", i, err) + } else if len(all) != 0 { + t.Errorf("Bogus body %q", all) + } } } @@ -553,12 +560,13 @@ func TestRoundTripGzip(t *testing.T) { res, err := DefaultTransport.RoundTrip(req) var body []byte if test.compressed { - gzip, err := gzip.NewReader(res.Body) + var r *gzip.Reader + r, err = gzip.NewReader(res.Body) if err != nil { t.Errorf("%d. gzip NewReader: %v", i, err) continue } - body, err = ioutil.ReadAll(gzip) + body, err = ioutil.ReadAll(r) res.Body.Close() } else { body, err = ioutil.ReadAll(res.Body) @@ -585,13 +593,16 @@ func TestTransportGzip(t *testing.T) { const testString = "The test string aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" const nRandBytes = 1024 * 1024 ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) { + if req.Method == "HEAD" { + if g := req.Header.Get("Accept-Encoding"); g != "" { + t.Errorf("HEAD request sent with Accept-Encoding of %q; want none", g) + } + return + } if g, e := req.Header.Get("Accept-Encoding"), "gzip"; g != e { t.Errorf("Accept-Encoding = %q, want %q", g, e) } rw.Header().Set("Content-Encoding", "gzip") - if req.Method == "HEAD" { - return - } var w io.Writer = rw var buf bytes.Buffer @@ -819,7 +830,7 @@ func TestTransportPersistConnLeakShortBody(t *testing.T) { } nhigh := runtime.NumGoroutine() tr.CloseIdleConnections() - time.Sleep(50 * time.Millisecond) + time.Sleep(400 * time.Millisecond) runtime.GC() nfinal := runtime.NumGoroutine() @@ -1571,6 +1582,77 @@ func TestProxyFromEnvironment(t *testing.T) { } } +func TestIdleConnChannelLeak(t *testing.T) { + var mu sync.Mutex + var n int + + ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) { + mu.Lock() + n++ + mu.Unlock() + })) + defer ts.Close() + + tr := &Transport{ + Dial: func(netw, addr string) (net.Conn, error) { + return net.Dial(netw, ts.Listener.Addr().String()) + }, + } + defer tr.CloseIdleConnections() + + c := &Client{Transport: tr} + + // First, without keep-alives. + for _, disableKeep := range []bool{true, false} { + tr.DisableKeepAlives = disableKeep + for i := 0; i < 5; i++ { + _, err := c.Get(fmt.Sprintf("http://foo-host-%d.tld/", i)) + if err != nil { + t.Fatal(err) + } + } + if got := tr.IdleConnChMapSizeForTesting(); got != 0 { + t.Fatalf("ForDisableKeepAlives = %v, map size = %d; want 0", disableKeep, got) + } + } +} + +// Verify the status quo: that the Client.Post function coerces its +// body into a ReadCloser if it's a Closer, and that the Transport +// then closes it. +func TestTransportClosesRequestBody(t *testing.T) { + defer afterTest(t) + ts := httptest.NewServer(http.HandlerFunc(func(w ResponseWriter, r *Request) { + io.Copy(ioutil.Discard, r.Body) + })) + defer ts.Close() + + tr := &Transport{} + defer tr.CloseIdleConnections() + cl := &Client{Transport: tr} + + closes := 0 + + res, err := cl.Post(ts.URL, "text/plain", countCloseReader{&closes, strings.NewReader("hello")}) + if err != nil { + t.Fatal(err) + } + res.Body.Close() + if closes != 1 { + t.Errorf("closes = %d; want 1", closes) + } +} + +type countCloseReader struct { + n *int + io.Reader +} + +func (cr countCloseReader) Close() error { + (*cr.n)++ + return nil +} + // rgz is a gzip quine that uncompresses to itself. var rgz = []byte{ 0x1f, 0x8b, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, diff --git a/libgo/go/net/http/z_last_test.go b/libgo/go/net/http/z_last_test.go index 2161db7..5a0cc11 100644 --- a/libgo/go/net/http/z_last_test.go +++ b/libgo/go/net/http/z_last_test.go @@ -23,7 +23,6 @@ func interestingGoroutines() (gs []string) { } stack := strings.TrimSpace(sl[1]) if stack == "" || - strings.Contains(stack, "created by net.newPollServer") || strings.Contains(stack, "created by net.startServer") || strings.Contains(stack, "created by testing.RunTests") || strings.Contains(stack, "closeWriteAndWait") || diff --git a/libgo/go/net/interface_bsd.go b/libgo/go/net/interface_bsd.go index 716b60a..1677557 100644 --- a/libgo/go/net/interface_bsd.go +++ b/libgo/go/net/interface_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/libgo/go/net/interface_dragonfly.go b/libgo/go/net/interface_dragonfly.go new file mode 100644 index 0000000..c9ce5a7 --- /dev/null +++ b/libgo/go/net/interface_dragonfly.go @@ -0,0 +1,12 @@ +// 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 + +// interfaceMulticastAddrTable returns addresses for a specific +// interface. +func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) { + // TODO(mikio): Implement this like other platforms. + return nil, nil +} diff --git a/libgo/go/net/interface_test.go b/libgo/go/net/interface_test.go index e31894a..efabb5f 100644 --- a/libgo/go/net/interface_test.go +++ b/libgo/go/net/interface_test.go @@ -108,12 +108,23 @@ func testInterfaceMulticastAddrs(t *testing.T, ifi *Interface) { func testAddrs(t *testing.T, ifat []Addr) { for _, ifa := range ifat { switch ifa := ifa.(type) { - case *IPAddr, *IPNet: - if ifa == nil { - t.Errorf("\tunexpected value: %v", ifa) + case *IPAddr: + if ifa == nil || ifa.IP == nil { + t.Errorf("\tunexpected value: %v, %v", ifa, ifa.IP) } else { t.Logf("\tinterface address %q", ifa.String()) } + case *IPNet: + if ifa == nil || ifa.IP == nil || ifa.Mask == nil { + t.Errorf("\tunexpected value: %v, %v, %v", ifa, ifa.IP, ifa.Mask) + } else { + _, prefixLen := ifa.Mask.Size() + if ifa.IP.To4() != nil && prefixLen != 8*IPv4len || ifa.IP.To16() != nil && ifa.IP.To4() == nil && prefixLen != 8*IPv6len { + t.Errorf("\tunexpected value: %v, %v, %v, %v", ifa, ifa.IP, ifa.Mask, prefixLen) + } else { + t.Logf("\tinterface address %q", ifa.String()) + } + } default: t.Errorf("\tunexpected type: %T", ifa) } diff --git a/libgo/go/net/ip.go b/libgo/go/net/ip.go index 0e42da2..fd6a7d4 100644 --- a/libgo/go/net/ip.go +++ b/libgo/go/net/ip.go @@ -12,6 +12,8 @@ package net +import "errors" + // IP address lengths (bytes). const ( IPv4len = 4 @@ -310,6 +312,43 @@ func (ip IP) String() string { return s } +// ipEmptyString is like ip.String except that it returns +// an empty string when ip is unset. +func ipEmptyString(ip IP) string { + if len(ip) == 0 { + return "" + } + return ip.String() +} + +// MarshalText implements the encoding.TextMarshaler interface. +// The encoding is the same as returned by String. +func (ip IP) MarshalText() ([]byte, error) { + if len(ip) == 0 { + return []byte(""), nil + } + if len(ip) != IPv4len && len(ip) != IPv6len { + return nil, errors.New("invalid IP address") + } + return []byte(ip.String()), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The IP address is expected in a form accepted by ParseIP. +func (ip *IP) UnmarshalText(text []byte) error { + if len(text) == 0 { + *ip = nil + return nil + } + s := string(text) + x := ParseIP(s) + if x == nil { + return &ParseError{"IP address", s} + } + *ip = x + return nil +} + // Equal returns true if ip and x are the same IP address. // An IPv4 address and that same address in IPv6 form are // considered to be equal. diff --git a/libgo/go/net/ip_test.go b/libgo/go/net/ip_test.go index 16f30d4..26b5372 100644 --- a/libgo/go/net/ip_test.go +++ b/libgo/go/net/ip_test.go @@ -32,6 +32,32 @@ func TestParseIP(t *testing.T) { if out := ParseIP(tt.in); !reflect.DeepEqual(out, tt.out) { t.Errorf("ParseIP(%q) = %v, want %v", tt.in, out, tt.out) } + if tt.in == "" { + // Tested in TestMarshalEmptyIP below. + continue + } + var out IP + if err := out.UnmarshalText([]byte(tt.in)); !reflect.DeepEqual(out, tt.out) || (tt.out == nil) != (err != nil) { + t.Errorf("IP.UnmarshalText(%q) = %v, %v, want %v", tt.in, out, err, tt.out) + } + } +} + +// Issue 6339 +func TestMarshalEmptyIP(t *testing.T) { + for _, in := range [][]byte{nil, []byte("")} { + var out = IP{1, 2, 3, 4} + if err := out.UnmarshalText(in); err != nil || out != nil { + t.Errorf("UnmarshalText(%v) = %v, %v; want nil, nil", in, out, err) + } + } + var ip IP + got, err := ip.MarshalText() + if err != nil { + t.Fatal(err) + } + if !reflect.DeepEqual(got, []byte("")) { + t.Errorf(`got %#v, want []byte("")`, got) } } @@ -47,13 +73,19 @@ var ipStringTests = []struct { {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0}, "2001:db8:0:0:1::"}, {IP{0x20, 0x1, 0xd, 0xb8, 0, 0, 0, 0, 0, 0x1, 0, 0, 0, 0, 0, 0x1}, "2001:db8::1:0:0:1"}, {IP{0x20, 0x1, 0xD, 0xB8, 0, 0, 0, 0, 0, 0xA, 0, 0xB, 0, 0xC, 0, 0xD}, "2001:db8::a:b:c:d"}, - {nil, "<nil>"}, + {IPv4(192, 168, 0, 1), "192.168.0.1"}, + {nil, ""}, } func TestIPString(t *testing.T) { for _, tt := range ipStringTests { - if out := tt.in.String(); out != tt.out { - t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.out) + if tt.in != nil { + if out := tt.in.String(); out != tt.out { + t.Errorf("IP.String(%v) = %q, want %q", tt.in, out, tt.out) + } + } + if out, err := tt.in.MarshalText(); string(out) != tt.out || err != nil { + t.Errorf("IP.MarshalText(%v) = %q, %v, want %q, nil", tt.in, out, err, tt.out) } } } diff --git a/libgo/go/net/ipraw_test.go b/libgo/go/net/ipraw_test.go index 12c199d..ea183f1 100644 --- a/libgo/go/net/ipraw_test.go +++ b/libgo/go/net/ipraw_test.go @@ -6,19 +6,19 @@ package net import ( "bytes" - "errors" "fmt" "os" "reflect" + "runtime" "testing" "time" ) type resolveIPAddrTest struct { - net string - litAddr string - addr *IPAddr - err error + net string + litAddrOrName string + addr *IPAddr + err error } var resolveIPAddrTests = []resolveIPAddrTest{ @@ -29,6 +29,7 @@ var resolveIPAddrTests = []resolveIPAddrTest{ {"ip", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, {"ip6", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, {"ip6:ipv6-icmp", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, + {"ip6:IPv6-ICMP", "::1", &IPAddr{IP: ParseIP("::1")}, nil}, {"ip", "::1%en0", &IPAddr{IP: ParseIP("::1"), Zone: "en0"}, nil}, {"ip6", "::1%911", &IPAddr{IP: ParseIP("::1"), Zone: "911"}, nil}, @@ -49,13 +50,28 @@ func init() { {"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil}, }...) } + if ips, err := LookupIP("localhost"); err == nil && len(ips) > 1 && supportsIPv4 && supportsIPv6 { + resolveIPAddrTests = append(resolveIPAddrTests, []resolveIPAddrTest{ + {"ip", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + {"ip4", "localhost", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, + {"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil}, + }...) + } +} + +func skipRawSocketTest(t *testing.T) (skip bool, skipmsg string) { + skip, skipmsg, err := skipRawSocketTests() + if err != nil { + t.Fatal(err) + } + return skip, skipmsg } func TestResolveIPAddr(t *testing.T) { for _, tt := range resolveIPAddrTests { - addr, err := ResolveIPAddr(tt.net, tt.litAddr) + addr, err := ResolveIPAddr(tt.net, tt.litAddrOrName) if err != tt.err { - condFatalf(t, "ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddrOrName, err) } else if !reflect.DeepEqual(addr, tt.addr) { t.Fatalf("got %#v; expected %#v", addr, tt.addr) } @@ -72,8 +88,8 @@ var icmpEchoTests = []struct { } func TestConnICMPEcho(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("skipping test; must be root") + if skip, skipmsg := skipRawSocketTest(t); skip { + t.Skip(skipmsg) } for i, tt := range icmpEchoTests { @@ -97,7 +113,7 @@ func TestConnICMPEcho(t *testing.T) { typ = icmpv6EchoRequest } xid, xseq := os.Getpid()&0xffff, i+1 - b, err := (&icmpMessage{ + wb, err := (&icmpMessage{ Type: typ, Code: 0, Body: &icmpEcho{ ID: xid, Seq: xseq, @@ -107,18 +123,19 @@ func TestConnICMPEcho(t *testing.T) { if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } - if _, err := c.Write(b); err != nil { + if _, err := c.Write(wb); err != nil { t.Fatalf("Conn.Write failed: %v", err) } var m *icmpMessage + rb := make([]byte, 20+len(wb)) for { - if _, err := c.Read(b); err != nil { + if _, err := c.Read(rb); err != nil { t.Fatalf("Conn.Read failed: %v", err) } if net == "ip4" { - b = ipv4Payload(b) + rb = ipv4Payload(rb) } - if m, err = parseICMPMessage(b); err != nil { + if m, err = parseICMPMessage(rb); err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } switch m.Type { @@ -139,8 +156,8 @@ func TestConnICMPEcho(t *testing.T) { } func TestPacketConnICMPEcho(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("skipping test; must be root") + if skip, skipmsg := skipRawSocketTest(t); skip { + t.Skip(skipmsg) } for i, tt := range icmpEchoTests { @@ -168,7 +185,7 @@ func TestPacketConnICMPEcho(t *testing.T) { typ = icmpv6EchoRequest } xid, xseq := os.Getpid()&0xffff, i+1 - b, err := (&icmpMessage{ + wb, err := (&icmpMessage{ Type: typ, Code: 0, Body: &icmpEcho{ ID: xid, Seq: xseq, @@ -178,19 +195,20 @@ func TestPacketConnICMPEcho(t *testing.T) { if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } - if _, err := c.WriteTo(b, ra); err != nil { + if _, err := c.WriteTo(wb, ra); err != nil { t.Fatalf("PacketConn.WriteTo failed: %v", err) } var m *icmpMessage + rb := make([]byte, 20+len(wb)) for { - if _, _, err := c.ReadFrom(b); err != nil { + if _, _, err := c.ReadFrom(rb); err != nil { t.Fatalf("PacketConn.ReadFrom failed: %v", err) } - // TODO: fix issue 3944 + // See BUG section. //if net == "ip4" { - // b = ipv4Payload(b) + // rb = ipv4Payload(rb) //} - if m, err = parseICMPMessage(b); err != nil { + if m, err = parseICMPMessage(rb); err != nil { t.Fatalf("parseICMPMessage failed: %v", err) } switch m.Type { @@ -218,115 +236,6 @@ func ipv4Payload(b []byte) []byte { return b[hdrlen:] } -const ( - icmpv4EchoRequest = 8 - icmpv4EchoReply = 0 - icmpv6EchoRequest = 128 - icmpv6EchoReply = 129 -) - -// icmpMessage represents an ICMP message. -type icmpMessage struct { - Type int // type - Code int // code - Checksum int // checksum - Body icmpMessageBody // body -} - -// icmpMessageBody represents an ICMP message body. -type icmpMessageBody interface { - Len() int - Marshal() ([]byte, error) -} - -// Marshal returns the binary enconding of the ICMP echo request or -// reply message m. -func (m *icmpMessage) Marshal() ([]byte, error) { - b := []byte{byte(m.Type), byte(m.Code), 0, 0} - if m.Body != nil && m.Body.Len() != 0 { - mb, err := m.Body.Marshal() - if err != nil { - return nil, err - } - b = append(b, mb...) - } - switch m.Type { - case icmpv6EchoRequest, icmpv6EchoReply: - return b, nil - } - csumcv := len(b) - 1 // checksum coverage - s := uint32(0) - for i := 0; i < csumcv; i += 2 { - s += uint32(b[i+1])<<8 | uint32(b[i]) - } - if csumcv&1 == 0 { - s += uint32(b[csumcv]) - } - s = s>>16 + s&0xffff - s = s + s>>16 - // Place checksum back in header; using ^= avoids the - // assumption the checksum bytes are zero. - b[2] ^= byte(^s & 0xff) - b[3] ^= byte(^s >> 8) - return b, nil -} - -// parseICMPMessage parses b as an ICMP message. -func parseICMPMessage(b []byte) (*icmpMessage, error) { - msglen := len(b) - if msglen < 4 { - return nil, errors.New("message too short") - } - m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])} - if msglen > 4 { - var err error - switch m.Type { - case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply: - m.Body, err = parseICMPEcho(b[4:]) - if err != nil { - return nil, err - } - } - } - return m, nil -} - -// imcpEcho represenets an ICMP echo request or reply message body. -type icmpEcho struct { - ID int // identifier - Seq int // sequence number - Data []byte // data -} - -func (p *icmpEcho) Len() int { - if p == nil { - return 0 - } - return 4 + len(p.Data) -} - -// Marshal returns the binary enconding of the ICMP echo request or -// reply message body p. -func (p *icmpEcho) Marshal() ([]byte, error) { - b := make([]byte, 4+len(p.Data)) - b[0], b[1] = byte(p.ID>>8), byte(p.ID&0xff) - b[2], b[3] = byte(p.Seq>>8), byte(p.Seq&0xff) - copy(b[4:], p.Data) - return b, nil -} - -// parseICMPEcho parses b as an ICMP echo request or reply message -// body. -func parseICMPEcho(b []byte) (*icmpEcho, error) { - bodylen := len(b) - p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])} - if bodylen > 4 { - p.Data = make([]byte, bodylen-4) - copy(p.Data, b[4:]) - } - return p, nil -} - var ipConnLocalNameTests = []struct { net string laddr *IPAddr @@ -337,8 +246,13 @@ var ipConnLocalNameTests = []struct { } func TestIPConnLocalName(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("skipping test; must be root") + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) + default: + if os.Getuid() != 0 { + t.Skip("skipping test; must be root") + } } for _, tt := range ipConnLocalNameTests { @@ -354,8 +268,13 @@ func TestIPConnLocalName(t *testing.T) { } func TestIPConnRemoteName(t *testing.T) { - if os.Getuid() != 0 { - t.Skip("skipping test; must be root") + switch runtime.GOOS { + case "plan9", "windows": + t.Skipf("skipping test on %q", runtime.GOOS) + default: + if os.Getuid() != 0 { + t.Skip("skipping test; must be root") + } } raddr := &IPAddr{IP: IPv4(127, 0, 0, 10).To4()} diff --git a/libgo/go/net/iprawsock.go b/libgo/go/net/iprawsock.go index 0be94eb..5cc3613 100644 --- a/libgo/go/net/iprawsock.go +++ b/libgo/go/net/iprawsock.go @@ -23,6 +23,13 @@ func (a *IPAddr) String() string { return a.IP.String() } +func (a *IPAddr) toAddr() Addr { + if a == nil { + return nil + } + return a +} + // ResolveIPAddr parses addr as an IP address of the form "host" or // "ipv6-host%zone" and resolves the domain name on the network net, // which must be "ip", "ip4" or "ip6". @@ -43,5 +50,5 @@ func ResolveIPAddr(net, addr string) (*IPAddr, error) { if err != nil { return nil, err } - return a.(*IPAddr), nil + return a.toAddr().(*IPAddr), nil } diff --git a/libgo/go/net/iprawsock_posix.go b/libgo/go/net/iprawsock_posix.go index caeeb46..7228532 100644 --- a/libgo/go/net/iprawsock_posix.go +++ b/libgo/go/net/iprawsock_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -11,6 +11,18 @@ import ( "time" ) +// BUG(mikio): On every POSIX platform, reads from the "ip4" network +// using the ReadFrom or ReadFromIP method might not return a complete +// IPv4 packet, including its header, even if there is space +// available. This can occur even in cases where Read or ReadMsgIP +// could return a complete packet. For this reason, it is recommended +// that you do not uses these methods if it is important to receive a +// full packet. +// +// The Go 1 compatibliity guidelines make it impossible for us to +// change the behavior of these methods; use Read or ReadMsgIP +// instead. + func sockaddrToIP(sa syscall.Sockaddr) Addr { switch sa := sa.(type) { case *syscall.SockaddrInet4: @@ -39,14 +51,10 @@ func (a *IPAddr) isWildcard() bool { } func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, 0, a.Zone) -} - -func (a *IPAddr) toAddr() sockaddr { - if a == nil { // nil *IPAddr - return nil // nil interface + if a == nil { + return nil, nil } - return a + return ipToSockaddr(family, a.IP, 0, a.Zone) } // IPConn is the implementation of the Conn and PacketConn interfaces @@ -125,6 +133,9 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) { if !c.ok() { return 0, syscall.EINVAL } + if addr == nil { + return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress} + } sa, err := addr.sockaddr(c.fd.family) if err != nil { return 0, &OpError{"write", c.fd.net, addr, err} @@ -151,6 +162,9 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error if !c.ok() { return 0, 0, syscall.EINVAL } + if addr == nil { + return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress} + } sa, err := addr.sockaddr(c.fd.family) if err != nil { return 0, 0, &OpError{"write", c.fd.net, addr, err} @@ -168,19 +182,19 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) { func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) { net, proto, err := parseNetwork(netProto) if err != nil { - return nil, err + return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: err} } switch net { case "ip", "ip4", "ip6": default: - return nil, UnknownNetworkError(netProto) + return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: UnknownNetworkError(netProto)} } if raddr == nil { - return nil, &OpError{"dial", netProto, nil, errMissingAddress} + return nil, &OpError{Op: "dial", Net: netProto, Addr: nil, Err: errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) + fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP) if err != nil { - return nil, err + return nil, &OpError{Op: "dial", Net: netProto, Addr: raddr, Err: err} } return newIPConn(fd), nil } @@ -192,16 +206,16 @@ func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) { net, proto, err := parseNetwork(netProto) if err != nil { - return nil, err + return nil, &OpError{Op: "dial", Net: netProto, Addr: laddr, Err: err} } switch net { case "ip", "ip4", "ip6": default: - return nil, UnknownNetworkError(netProto) + return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: UnknownNetworkError(netProto)} } - fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP) + fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP) if err != nil { - return nil, err + return nil, &OpError{Op: "listen", Net: netProto, Addr: laddr, Err: err} } return newIPConn(fd), nil } diff --git a/libgo/go/net/ipsock.go b/libgo/go/net/ipsock.go index d930595..8b586ef 100644 --- a/libgo/go/net/ipsock.go +++ b/libgo/go/net/ipsock.go @@ -6,68 +6,135 @@ package net -import "time" +import ( + "errors" + "time" +) -var supportsIPv6, supportsIPv4map bool +var ( + // supportsIPv4 reports whether the platform supports IPv4 + // networking functionality. + supportsIPv4 bool + + // supportsIPv6 reports whether the platfrom supports IPv6 + // networking functionality. + supportsIPv6 bool + + // supportsIPv4map reports whether the platform supports + // mapping an IPv4 address inside an IPv6 address at transport + // layer protocols. See RFC 4291, RFC 4038 and RFC 3493. + supportsIPv4map bool +) func init() { sysInit() + supportsIPv4 = probeIPv4Stack() supportsIPv6, supportsIPv4map = probeIPv6Stack() } -func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) { - if filter == nil { - // We'll take any IP address, but since the dialing code - // does not yet try multiple addresses, prefer to use - // an IPv4 address if possible. This is especially relevant - // if localhost resolves to [ipv6-localhost, ipv4-localhost]. - // Too much code assumes localhost == ipv4-localhost. - addr = firstSupportedAddr(ipv4only, addrs) - if addr == nil { - addr = firstSupportedAddr(anyaddr, addrs) - } - } else { - addr = firstSupportedAddr(filter, addrs) +// A netaddr represents a network endpoint address or a list of +// network endpoint addresses. +type netaddr interface { + // toAddr returns the address represented in Addr interface. + // It returns a nil interface when the address is nil. + toAddr() Addr +} + +// An addrList represents a list of network endpoint addresses. +type addrList []netaddr + +func (al addrList) toAddr() Addr { + switch len(al) { + case 0: + return nil + case 1: + return al[0].toAddr() + default: + // For now, we'll roughly pick first one without + // considering dealing with any preferences such as + // DNS TTL, transport path quality, network routing + // information. + return al[0].toAddr() } - return } -func firstSupportedAddr(filter func(IP) IP, addrs []string) IP { - for _, s := range addrs { - if addr := filter(ParseIP(s)); addr != nil { - return addr +var errNoSuitableAddress = errors.New("no suitable address found") + +// firstFavoriteAddr returns an address or a list of addresses that +// implement the netaddr interface. Known filters are nil, ipv4only +// and ipv6only. It returns any address when filter is nil. The result +// contains at least one address when error is nil. +func firstFavoriteAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) { + if filter != nil { + return firstSupportedAddr(filter, ips, inetaddr) + } + var ( + ipv4, ipv6, swap bool + list addrList + ) + for _, ip := range ips { + // We'll take any IP address, but since the dialing + // code does not yet try multiple addresses + // effectively, prefer to use an IPv4 address if + // possible. This is especially relevant if localhost + // resolves to [ipv6-localhost, ipv4-localhost]. Too + // much code assumes localhost == ipv4-localhost. + if ip4 := ipv4only(ip); ip4 != nil && !ipv4 { + list = append(list, inetaddr(ip4)) + ipv4 = true + if ipv6 { + swap = true + } + } else if ip6 := ipv6only(ip); ip6 != nil && !ipv6 { + list = append(list, inetaddr(ip6)) + ipv6 = true + } + if ipv4 && ipv6 { + if swap { + list[0], list[1] = list[1], list[0] + } + break } } - return nil + switch len(list) { + case 0: + return nil, errNoSuitableAddress + case 1: + return list[0], nil + default: + return list, nil + } } -func anyaddr(x IP) IP { - if x4 := x.To4(); x4 != nil { - return x4 +func firstSupportedAddr(filter func(IP) IP, ips []IP, inetaddr func(IP) netaddr) (netaddr, error) { + for _, ip := range ips { + if ip := filter(ip); ip != nil { + return inetaddr(ip), nil + } } - if supportsIPv6 { - return x + return nil, errNoSuitableAddress +} + +// ipv4only returns IPv4 addresses that we can use with the kernel's +// IPv4 addressing modes. If ip is an IPv4 address, ipv4only returns ip. +// Otherwise it returns nil. +func ipv4only(ip IP) IP { + if supportsIPv4 && ip.To4() != nil { + return ip } return nil } -func ipv4only(x IP) IP { return x.To4() } - -func ipv6only(x IP) IP { - // Only return addresses that we can use - // with the kernel's IPv6 addressing modes. - if len(x) == IPv6len && x.To4() == nil && supportsIPv6 { - return x +// ipv6only returns IPv6 addresses that we can use with the kernel's +// IPv6 addressing modes. It returns IPv4-mapped IPv6 addresses as +// nils and returns other IPv6 address types as IPv6 addresses. +func ipv6only(ip IP) IP { + if supportsIPv6 && len(ip) == IPv6len && ip.To4() == nil { + return ip } return nil } -type InvalidAddrError string - -func (e InvalidAddrError) Error() string { return string(e) } -func (e InvalidAddrError) Timeout() bool { return false } -func (e InvalidAddrError) Temporary() bool { return false } - // SplitHostPort splits a network address of the form "host:port", // "[host]:port" or "[ipv6-host%zone]:port" into host or // ipv6-host%zone and port. A literal address or host name for IPv6 @@ -161,7 +228,13 @@ func JoinHostPort(host, port string) string { return host + ":" + port } -func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { +// resolveInternetAddr resolves addr that is either a literal IP +// 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 +// address when error is nil. +func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) { var ( err error host, port, zone string @@ -184,30 +257,32 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { default: return nil, UnknownNetworkError(net) } - inetaddr := func(net string, ip IP, port int, zone string) Addr { + inetaddr := func(ip IP) netaddr { switch net { case "tcp", "tcp4", "tcp6": - return &TCPAddr{IP: ip, Port: port, Zone: zone} + return &TCPAddr{IP: ip, Port: portnum, Zone: zone} case "udp", "udp4", "udp6": - return &UDPAddr{IP: ip, Port: port, Zone: zone} + return &UDPAddr{IP: ip, Port: portnum, Zone: zone} case "ip", "ip4", "ip6": return &IPAddr{IP: ip, Zone: zone} + default: + panic("unexpected network: " + net) } - return nil } if host == "" { - return inetaddr(net, nil, portnum, zone), nil + return inetaddr(nil), nil } - // Try as an IP address. - if ip := parseIPv4(host); ip != nil { - return inetaddr(net, ip, portnum, zone), nil + // Try as a literal IP address. + var ip IP + if ip = parseIPv4(host); ip != nil { + return inetaddr(ip), nil } - if ip, zone := parseIPv6(host, true); ip != nil { - return inetaddr(net, ip, portnum, zone), nil + if ip, zone = parseIPv6(host, true); ip != nil { + return inetaddr(ip), nil } - // Try as a domain name. + // Try as a DNS name. host, zone = splitHostZone(host) - addrs, err := lookupHostDeadline(host, deadline) + ips, err := lookupIPDeadline(host, deadline) if err != nil { return nil, err } @@ -218,12 +293,7 @@ func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) { if net != "" && net[len(net)-1] == '6' || zone != "" { filter = ipv6only } - ip := firstFavoriteAddr(filter, addrs) - if ip == nil { - // should not happen - return nil, &AddrError{"LookupHost returned no suitable address", addrs[0]} - } - return inetaddr(net, ip, portnum, zone), nil + return firstFavoriteAddr(filter, ips, inetaddr) } func zoneToString(zone int) string { diff --git a/libgo/go/net/ipsock_plan9.go b/libgo/go/net/ipsock_plan9.go index c7d542d..fcec416 100644 --- a/libgo/go/net/ipsock_plan9.go +++ b/libgo/go/net/ipsock_plan9.go @@ -12,13 +12,18 @@ import ( "syscall" ) -// /sys/include/ape/sys/socket.h:/SOMAXCONN -var listenerBacklog = 5 +func probeIPv4Stack() bool { + // TODO(mikio): implement this when Plan 9 supports IPv6-only + // kernel. + return true +} // probeIPv6Stack returns two boolean values. If the first boolean // value is true, kernel supports basic IPv6 functionality. If the // second boolean value is true, kernel supports IPv6 IPv4-mapping. func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { + // TODO(mikio): implement this once Plan 9 gets an IPv6 + // protocol stack implementation. return false, false } diff --git a/libgo/go/net/ipsock_posix.go b/libgo/go/net/ipsock_posix.go index 4c37616..a83e525 100644 --- a/libgo/go/net/ipsock_posix.go +++ b/libgo/go/net/ipsock_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows // Internet protocol family sockets for POSIX @@ -13,6 +13,17 @@ import ( "time" ) +func probeIPv4Stack() bool { + s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP) + switch err { + case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT: + return false + case nil: + closesocket(s) + } + return true +} + // Should we try to use the IPv4 socket interface if we're // only dealing with IPv4 sockets? As long as the host system // understands IPv6, it's okay to pass IPv4 addresses to the IPv6 @@ -28,8 +39,8 @@ import ( // boolean value is true, kernel supports IPv6 IPv4-mapping. func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { var probes = []struct { - la TCPAddr - ok bool + laddr TCPAddr + ok bool }{ // IPv6 communication capability {TCPAddr{IP: ParseIP("::1")}, false}, @@ -44,12 +55,11 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) { } defer closesocket(s) syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0) - sa, err := probes[i].la.toAddr().sockaddr(syscall.AF_INET6) + sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6) if err != nil { continue } - err = syscall.Bind(s, sa) - if err != nil { + if err := syscall.Bind(s, sa); err != nil { continue } probes[i].ok = true @@ -121,40 +131,9 @@ func favoriteAddrFamily(net string, laddr, raddr sockaddr, mode string) (family // Internet sockets (TCP, UDP, IP) -// A sockaddr represents a TCP, UDP or IP network address that can -// be converted into a syscall.Sockaddr. -type sockaddr interface { - Addr - family() int - isWildcard() bool - sockaddr(family int) (syscall.Sockaddr, error) -} - func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { - var la, ra syscall.Sockaddr family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode) - if laddr != nil { - if la, err = laddr.sockaddr(family); err != nil { - goto Error - } - } - if raddr != nil { - if ra, err = raddr.sockaddr(family); err != nil { - goto Error - } - } - fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, deadline, toAddr) - if err != nil { - goto Error - } - return fd, nil - -Error: - addr := raddr - if mode == "listen" { - addr = laddr - } - return nil, &OpError{mode, net, addr, err} + return socket(net, family, sotype, proto, ipv6only, laddr, raddr, deadline, toAddr) } func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) { diff --git a/libgo/go/net/ipsock_test.go b/libgo/go/net/ipsock_test.go new file mode 100644 index 0000000..9ecaaec --- /dev/null +++ b/libgo/go/net/ipsock_test.go @@ -0,0 +1,193 @@ +// 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 ( + "reflect" + "testing" +) + +var testInetaddr = func(ip IP) netaddr { return &TCPAddr{IP: ip, Port: 5682} } + +var firstFavoriteAddrTests = []struct { + filter func(IP) IP + ips []IP + inetaddr func(IP) netaddr + addr netaddr + err error +}{ + { + nil, + []IP{ + IPv4(127, 0, 0, 1), + IPv6loopback, + }, + testInetaddr, + addrList{ + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + }, + nil, + }, + { + nil, + []IP{ + IPv6loopback, + IPv4(127, 0, 0, 1), + }, + testInetaddr, + addrList{ + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + }, + nil, + }, + { + nil, + []IP{ + IPv4(127, 0, 0, 1), + IPv4(192, 168, 0, 1), + }, + testInetaddr, + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + nil, + }, + { + nil, + []IP{ + IPv6loopback, + ParseIP("fe80::1"), + }, + testInetaddr, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + nil, + }, + { + nil, + []IP{ + IPv4(127, 0, 0, 1), + IPv4(192, 168, 0, 1), + IPv6loopback, + ParseIP("fe80::1"), + }, + testInetaddr, + addrList{ + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + }, + nil, + }, + { + nil, + []IP{ + IPv6loopback, + ParseIP("fe80::1"), + IPv4(127, 0, 0, 1), + IPv4(192, 168, 0, 1), + }, + testInetaddr, + addrList{ + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + }, + nil, + }, + { + nil, + []IP{ + IPv4(127, 0, 0, 1), + IPv6loopback, + IPv4(192, 168, 0, 1), + ParseIP("fe80::1"), + }, + testInetaddr, + addrList{ + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + }, + nil, + }, + { + nil, + []IP{ + IPv6loopback, + IPv4(127, 0, 0, 1), + ParseIP("fe80::1"), + IPv4(192, 168, 0, 1), + }, + testInetaddr, + addrList{ + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + }, + nil, + }, + + { + ipv4only, + []IP{ + IPv4(127, 0, 0, 1), + IPv6loopback, + }, + testInetaddr, + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + nil, + }, + { + ipv4only, + []IP{ + IPv6loopback, + IPv4(127, 0, 0, 1), + }, + testInetaddr, + &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5682}, + nil, + }, + + { + ipv6only, + []IP{ + IPv4(127, 0, 0, 1), + IPv6loopback, + }, + testInetaddr, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + nil, + }, + { + ipv6only, + []IP{ + IPv6loopback, + IPv4(127, 0, 0, 1), + }, + testInetaddr, + &TCPAddr{IP: IPv6loopback, Port: 5682}, + nil, + }, + + {nil, nil, testInetaddr, nil, errNoSuitableAddress}, + + {ipv4only, nil, testInetaddr, nil, errNoSuitableAddress}, + {ipv4only, []IP{IPv6loopback}, testInetaddr, nil, errNoSuitableAddress}, + + {ipv6only, nil, testInetaddr, nil, errNoSuitableAddress}, + {ipv6only, []IP{IPv4(127, 0, 0, 1)}, testInetaddr, nil, errNoSuitableAddress}, +} + +func TestFirstFavoriteAddr(t *testing.T) { + if !supportsIPv4 || !supportsIPv6 { + t.Skip("ipv4 or ipv6 is not supported") + } + + for i, tt := range firstFavoriteAddrTests { + addr, err := firstFavoriteAddr(tt.filter, tt.ips, tt.inetaddr) + if err != tt.err { + t.Errorf("#%v: got %v; expected %v", i, err, tt.err) + } + if !reflect.DeepEqual(addr, tt.addr) { + t.Errorf("#%v: got %v; expected %v", i, addr, tt.addr) + } + } +} diff --git a/libgo/go/net/lookup.go b/libgo/go/net/lookup.go index bec93ec..20f2057 100644 --- a/libgo/go/net/lookup.go +++ b/libgo/go/net/lookup.go @@ -4,9 +4,20 @@ package net -import ( - "time" -) +import "time" + +// protocols contains minimal mappings between internet protocol +// names and numbers for platforms that don't have a complete list of +// protocol numbers. +// +// See http://www.iana.org/assignments/protocol-numbers +var protocols = map[string]int{ + "icmp": 1, "ICMP": 1, + "igmp": 2, "IGMP": 2, + "tcp": 6, "TCP": 6, + "udp": 17, "UDP": 17, + "ipv6-icmp": 58, "IPV6-ICMP": 58, "IPv6-ICMP": 58, +} // LookupHost looks up the given host using the local resolver. // It returns an array of that host's addresses. @@ -14,9 +25,36 @@ func LookupHost(host string) (addrs []string, err error) { return lookupHost(host) } -func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err error) { +// LookupIP looks up host using the local resolver. +// It returns an array of that host's IPv4 and IPv6 addresses. +func LookupIP(host string) (addrs []IP, err error) { + return lookupIPMerge(host) +} + +var lookupGroup singleflight + +// lookupIPMerge wraps lookupIP, but makes sure that for any given +// host, only one lookup is in-flight at a time. The returned memory +// is always owned by the caller. +func lookupIPMerge(host string) (addrs []IP, err error) { + addrsi, err, shared := lookupGroup.Do(host, func() (interface{}, error) { + return lookupIP(host) + }) + if err != nil { + return nil, err + } + addrs = addrsi.([]IP) + if shared { + clone := make([]IP, len(addrs)) + copy(clone, addrs) + addrs = clone + } + return addrs, nil +} + +func lookupIPDeadline(host string, deadline time.Time) (addrs []IP, err error) { if deadline.IsZero() { - return lookupHost(host) + return lookupIPMerge(host) } // TODO(bradfitz): consider pushing the deadline down into the @@ -34,12 +72,12 @@ func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err er t := time.NewTimer(timeout) defer t.Stop() type res struct { - addrs []string + addrs []IP err error } resc := make(chan res, 1) go func() { - a, err := lookupHost(host) + a, err := lookupIPMerge(host) resc <- res{a, err} }() select { @@ -51,12 +89,6 @@ func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err er return } -// LookupIP looks up host using the local resolver. -// It returns an array of that host's IPv4 and IPv6 addresses. -func LookupIP(host string) (addrs []IP, err error) { - return lookupIP(host) -} - // LookupPort looks up the port for the given network and service. func LookupPort(network, service string) (port int, err error) { return lookupPort(network, service) diff --git a/libgo/go/net/lookup_plan9.go b/libgo/go/net/lookup_plan9.go index 94c5533..f1204a9 100644 --- a/libgo/go/net/lookup_plan9.go +++ b/libgo/go/net/lookup_plan9.go @@ -186,9 +186,9 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err if len(f) < 6 { continue } - port, _, portOk := dtoi(f[2], 0) + port, _, portOk := dtoi(f[4], 0) priority, _, priorityOk := dtoi(f[3], 0) - weight, _, weightOk := dtoi(f[4], 0) + weight, _, weightOk := dtoi(f[2], 0) if !(portOk && priorityOk && weightOk) { continue } @@ -224,10 +224,10 @@ func lookupNS(name string) (ns []*NS, err error) { } for _, line := range lines { f := getFields(line) - if len(f) < 4 { + if len(f) < 3 { continue } - ns = append(ns, &NS{f[3]}) + ns = append(ns, &NS{f[2]}) } return } diff --git a/libgo/go/net/lookup_unix.go b/libgo/go/net/lookup_unix.go index fa98eed..59e9f63 100644 --- a/libgo/go/net/lookup_unix.go +++ b/libgo/go/net/lookup_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package net @@ -11,15 +11,11 @@ import ( "sync" ) -var ( - protocols map[string]int - onceReadProtocols sync.Once -) +var onceReadProtocols sync.Once // readProtocols loads contents of /etc/protocols into protocols map // for quick access. func readProtocols() { - protocols = make(map[string]int) if file, err := open("/etc/protocols"); err == nil { for line, ok := file.readLine(); ok; line, ok = file.readLine() { // tcp 6 TCP # transmission control protocol @@ -31,9 +27,13 @@ func readProtocols() { continue } if proto, _, ok := dtoi(f[1], 0); ok { - protocols[f[0]] = proto + if _, ok := protocols[f[0]]; !ok { + protocols[f[0]] = proto + } for _, alias := range f[2:] { - protocols[alias] = proto + if _, ok := protocols[alias]; !ok { + protocols[alias] = proto + } } } } diff --git a/libgo/go/net/lookup_windows.go b/libgo/go/net/lookup_windows.go index 3b29724..1303642 100644 --- a/libgo/go/net/lookup_windows.go +++ b/libgo/go/net/lookup_windows.go @@ -34,12 +34,19 @@ func lookupProtocol(name string) (proto int, err error) { } ch := make(chan result) go func() { + acquireThread() + defer releaseThread() runtime.LockOSThread() defer runtime.UnlockOSThread() proto, err := getprotobyname(name) ch <- result{proto: proto, err: err} }() r := <-ch + if r.err != nil { + if proto, ok := protocols[name]; ok { + return proto, nil + } + } return r.proto, r.err } @@ -56,6 +63,7 @@ func lookupHost(name string) (addrs []string, err error) { } func gethostbyname(name string) (addrs []IP, err error) { + // caller already acquired thread h, err := syscall.GetHostByName(name) if err != nil { return nil, os.NewSyscallError("GetHostByName", err) @@ -83,6 +91,8 @@ func oldLookupIP(name string) (addrs []IP, err error) { } ch := make(chan result) go func() { + acquireThread() + defer releaseThread() runtime.LockOSThread() defer runtime.UnlockOSThread() addrs, err := gethostbyname(name) @@ -93,6 +103,8 @@ func oldLookupIP(name string) (addrs []IP, err error) { } func newLookupIP(name string) (addrs []IP, err error) { + acquireThread() + defer releaseThread() hints := syscall.AddrinfoW{ Family: syscall.AF_UNSPEC, Socktype: syscall.SOCK_STREAM, @@ -122,6 +134,8 @@ func newLookupIP(name string) (addrs []IP, err error) { } func getservbyname(network, service string) (port int, err error) { + acquireThread() + defer releaseThread() switch network { case "tcp4", "tcp6": network = "tcp" @@ -144,6 +158,8 @@ func oldLookupPort(network, service string) (port int, err error) { } ch := make(chan result) go func() { + acquireThread() + defer releaseThread() runtime.LockOSThread() defer runtime.UnlockOSThread() port, err := getservbyname(network, service) @@ -154,6 +170,8 @@ func oldLookupPort(network, service string) (port int, err error) { } func newLookupPort(network, service string) (port int, err error) { + acquireThread() + defer releaseThread() var stype int32 switch network { case "tcp4", "tcp6": @@ -188,6 +206,8 @@ func newLookupPort(network, service string) (port int, err error) { } func lookupCNAME(name string) (cname string, err error) { + acquireThread() + defer releaseThread() var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_CNAME, 0, nil, &r, nil) if e != nil { @@ -202,6 +222,8 @@ func lookupCNAME(name string) (cname string, err error) { } func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) { + acquireThread() + defer releaseThread() var target string if service == "" && proto == "" { target = name @@ -224,6 +246,8 @@ func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err err } func lookupMX(name string) (mx []*MX, err error) { + acquireThread() + defer releaseThread() var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_MX, 0, nil, &r, nil) if e != nil { @@ -240,6 +264,8 @@ func lookupMX(name string) (mx []*MX, err error) { } func lookupNS(name string) (ns []*NS, err error) { + acquireThread() + defer releaseThread() var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_NS, 0, nil, &r, nil) if e != nil { @@ -255,6 +281,8 @@ func lookupNS(name string) (ns []*NS, err error) { } func lookupTXT(name string) (txt []string, err error) { + acquireThread() + defer releaseThread() var r *syscall.DNSRecord e := syscall.DnsQuery(name, syscall.DNS_TYPE_TEXT, 0, nil, &r, nil) if e != nil { @@ -273,6 +301,8 @@ func lookupTXT(name string) (txt []string, err error) { } func lookupAddr(addr string) (name []string, err error) { + acquireThread() + defer releaseThread() arpa, err := reverseaddr(addr) if err != nil { return nil, err diff --git a/libgo/go/net/mail/message.go b/libgo/go/net/mail/message.go index 96c796e..dc2ab44 100644 --- a/libgo/go/net/mail/message.go +++ b/libgo/go/net/mail/message.go @@ -342,7 +342,9 @@ func (p *addrParser) consumePhrase() (phrase string, err error) { word, err = p.consumeQuotedString() } else { // atom - word, err = p.consumeAtom(false) + // We actually parse dot-atom here to be more permissive + // than what RFC 5322 specifies. + word, err = p.consumeAtom(true) } // RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s. @@ -519,7 +521,7 @@ func isAtext(c byte, dot bool) bool { return bytes.IndexByte(atextChars, c) >= 0 } -// isQtext returns true if c is an RFC 5322 qtest character. +// isQtext returns true if c is an RFC 5322 qtext character. func isQtext(c byte) bool { // Printable US-ASCII, excluding backslash or quote. if c == '\\' || c == '"' { diff --git a/libgo/go/net/mail/message_test.go b/libgo/go/net/mail/message_test.go index 2e746f4..3c037f3 100644 --- a/libgo/go/net/mail/message_test.go +++ b/libgo/go/net/mail/message_test.go @@ -225,6 +225,16 @@ func TestAddressParsing(t *testing.T) { }, }, }, + // Custom example with "." in name. For issue 4938 + { + `Asem H. <noreply@example.com>`, + []*Address{ + { + Name: `Asem H.`, + Address: "noreply@example.com", + }, + }, + }, } for _, test := range tests { if len(test.exp) == 1 { diff --git a/libgo/go/net/mockicmp_test.go b/libgo/go/net/mockicmp_test.go new file mode 100644 index 0000000..e742365 --- /dev/null +++ b/libgo/go/net/mockicmp_test.go @@ -0,0 +1,116 @@ +// 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 "errors" + +const ( + icmpv4EchoRequest = 8 + icmpv4EchoReply = 0 + icmpv6EchoRequest = 128 + icmpv6EchoReply = 129 +) + +// icmpMessage represents an ICMP message. +type icmpMessage struct { + Type int // type + Code int // code + Checksum int // checksum + Body icmpMessageBody // body +} + +// icmpMessageBody represents an ICMP message body. +type icmpMessageBody interface { + Len() int + Marshal() ([]byte, error) +} + +// Marshal returns the binary enconding of the ICMP echo request or +// reply message m. +func (m *icmpMessage) Marshal() ([]byte, error) { + b := []byte{byte(m.Type), byte(m.Code), 0, 0} + if m.Body != nil && m.Body.Len() != 0 { + mb, err := m.Body.Marshal() + if err != nil { + return nil, err + } + b = append(b, mb...) + } + switch m.Type { + case icmpv6EchoRequest, icmpv6EchoReply: + return b, nil + } + csumcv := len(b) - 1 // checksum coverage + s := uint32(0) + for i := 0; i < csumcv; i += 2 { + s += uint32(b[i+1])<<8 | uint32(b[i]) + } + if csumcv&1 == 0 { + s += uint32(b[csumcv]) + } + s = s>>16 + s&0xffff + s = s + s>>16 + // Place checksum back in header; using ^= avoids the + // assumption the checksum bytes are zero. + b[2] ^= byte(^s) + b[3] ^= byte(^s >> 8) + return b, nil +} + +// parseICMPMessage parses b as an ICMP message. +func parseICMPMessage(b []byte) (*icmpMessage, error) { + msglen := len(b) + if msglen < 4 { + return nil, errors.New("message too short") + } + m := &icmpMessage{Type: int(b[0]), Code: int(b[1]), Checksum: int(b[2])<<8 | int(b[3])} + if msglen > 4 { + var err error + switch m.Type { + case icmpv4EchoRequest, icmpv4EchoReply, icmpv6EchoRequest, icmpv6EchoReply: + m.Body, err = parseICMPEcho(b[4:]) + if err != nil { + return nil, err + } + } + } + return m, nil +} + +// imcpEcho represenets an ICMP echo request or reply message body. +type icmpEcho struct { + ID int // identifier + Seq int // sequence number + Data []byte // data +} + +func (p *icmpEcho) Len() int { + if p == nil { + return 0 + } + return 4 + len(p.Data) +} + +// Marshal returns the binary enconding of the ICMP echo request or +// reply message body p. +func (p *icmpEcho) Marshal() ([]byte, error) { + b := make([]byte, 4+len(p.Data)) + b[0], b[1] = byte(p.ID>>8), byte(p.ID) + b[2], b[3] = byte(p.Seq>>8), byte(p.Seq) + copy(b[4:], p.Data) + return b, nil +} + +// parseICMPEcho parses b as an ICMP echo request or reply message +// body. +func parseICMPEcho(b []byte) (*icmpEcho, error) { + bodylen := len(b) + p := &icmpEcho{ID: int(b[0])<<8 | int(b[1]), Seq: int(b[2])<<8 | int(b[3])} + if bodylen > 4 { + p.Data = make([]byte, bodylen-4) + copy(p.Data, b[4:]) + } + return p, nil +} diff --git a/libgo/go/net/mockserver_test.go b/libgo/go/net/mockserver_test.go new file mode 100644 index 0000000..68ded5d --- /dev/null +++ b/libgo/go/net/mockserver_test.go @@ -0,0 +1,82 @@ +// 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 "sync" + +type streamListener struct { + net, addr string + ln Listener +} + +type dualStackServer struct { + lnmu sync.RWMutex + lns []streamListener + port string + + cmu sync.RWMutex + cs []Conn // established connections at the passive open side +} + +func (dss *dualStackServer) buildup(server func(*dualStackServer, Listener)) error { + for i := range dss.lns { + go server(dss, dss.lns[i].ln) + } + return nil +} + +func (dss *dualStackServer) putConn(c Conn) error { + dss.cmu.Lock() + dss.cs = append(dss.cs, c) + dss.cmu.Unlock() + return nil +} + +func (dss *dualStackServer) teardownNetwork(net string) error { + dss.lnmu.Lock() + for i := range dss.lns { + if net == dss.lns[i].net && dss.lns[i].ln != nil { + dss.lns[i].ln.Close() + dss.lns[i].ln = nil + } + } + dss.lnmu.Unlock() + return nil +} + +func (dss *dualStackServer) teardown() error { + dss.lnmu.Lock() + for i := range dss.lns { + if dss.lns[i].ln != nil { + dss.lns[i].ln.Close() + } + } + dss.lnmu.Unlock() + dss.cmu.Lock() + for _, c := range dss.cs { + c.Close() + } + dss.cmu.Unlock() + return nil +} + +func newDualStackServer(lns []streamListener) (*dualStackServer, error) { + dss := &dualStackServer{lns: lns, port: "0"} + for i := range dss.lns { + ln, err := Listen(dss.lns[i].net, dss.lns[i].addr+":"+dss.port) + if err != nil { + dss.teardown() + return nil, err + } + dss.lns[i].ln = ln + if dss.port == "0" { + if _, dss.port, err = SplitHostPort(ln.Addr().String()); err != nil { + dss.teardown() + return nil, err + } + } + } + return dss, nil +} diff --git a/libgo/go/net/multicast_test.go b/libgo/go/net/multicast_test.go index 8ff02a3..5660fd4 100644 --- a/libgo/go/net/multicast_test.go +++ b/libgo/go/net/multicast_test.go @@ -158,7 +158,7 @@ func checkMulticastListener(c *UDPConn, ip IP) error { func multicastRIBContains(ip IP) (bool, error) { switch runtime.GOOS { - case "netbsd", "openbsd", "plan9", "solaris", "windows": + case "dragonfly", "netbsd", "openbsd", "plan9", "solaris", "windows": return true, nil // not implemented yet case "linux": if runtime.GOARCH == "arm" || runtime.GOARCH == "alpha" { diff --git a/libgo/go/net/net.go b/libgo/go/net/net.go index 72b2b64..2e6db55 100644 --- a/libgo/go/net/net.go +++ b/libgo/go/net/net.go @@ -46,7 +46,6 @@ import ( "errors" "io" "os" - "sync" "syscall" "time" ) @@ -160,7 +159,7 @@ func (c *conn) SetDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - return setDeadline(c.fd, t) + return c.fd.setDeadline(t) } // SetReadDeadline implements the Conn SetReadDeadline method. @@ -168,7 +167,7 @@ func (c *conn) SetReadDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - return setReadDeadline(c.fd, t) + return c.fd.setReadDeadline(t) } // SetWriteDeadline implements the Conn SetWriteDeadline method. @@ -176,7 +175,7 @@ func (c *conn) SetWriteDeadline(t time.Time) error { if !c.ok() { return syscall.EINVAL } - return setWriteDeadline(c.fd, t) + return c.fd.setWriteDeadline(t) } // SetReadBuffer sets the size of the operating system's @@ -259,6 +258,8 @@ type PacketConn interface { SetWriteDeadline(t time.Time) error } +var listenerBacklog = maxListenerBacklog() + // A Listener is a generic network listener for stream-oriented protocols. // // Multiple goroutines may invoke methods on a Listener simultaneously. @@ -370,6 +371,12 @@ func (e UnknownNetworkError) Error() string { return "unknown network " + stri func (e UnknownNetworkError) Temporary() bool { return false } func (e UnknownNetworkError) Timeout() bool { return false } +type InvalidAddrError string + +func (e InvalidAddrError) Error() string { return string(e) } +func (e InvalidAddrError) Timeout() bool { return false } +func (e InvalidAddrError) Temporary() bool { return false } + // DNSConfigError represents an error reading the machine's DNS configuration. type DNSConfigError struct { Err error @@ -393,35 +400,22 @@ func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) { return io.Copy(writerOnly{w}, r) } -// deadline is an atomically-accessed number of nanoseconds since 1970 -// or 0, if no deadline is set. -type deadline struct { - sync.Mutex - val int64 -} +// Limit the number of concurrent cgo-using goroutines, because +// each will block an entire operating system thread. The usual culprit +// is resolving many DNS names in separate goroutines but the DNS +// server is not responding. Then the many lookups each use a different +// thread, and the system or the program runs out of threads. -func (d *deadline) expired() bool { - t := d.value() - return t > 0 && time.Now().UnixNano() >= t -} +var threadLimit = make(chan struct{}, 500) -func (d *deadline) value() (v int64) { - d.Lock() - v = d.val - d.Unlock() - return -} +// Using send for acquire is fine here because we are not using this +// to protect any memory. All we care about is the number of goroutines +// making calls at a time. -func (d *deadline) set(v int64) { - d.Lock() - d.val = v - d.Unlock() +func acquireThread() { + threadLimit <- struct{}{} } -func (d *deadline) setTime(t time.Time) { - if t.IsZero() { - d.set(0) - } else { - d.set(t.UnixNano()) - } +func releaseThread() { + <-threadLimit } diff --git a/libgo/go/net/net_test.go b/libgo/go/net/net_test.go index 1a512a5..1320096 100644 --- a/libgo/go/net/net_test.go +++ b/libgo/go/net/net_test.go @@ -25,6 +25,7 @@ func TestShutdown(t *testing.T) { } go func() { + defer ln.Close() c, err := ln.Accept() if err != nil { t.Fatalf("Accept: %v", err) @@ -75,7 +76,10 @@ func TestShutdownUnix(t *testing.T) { if err != nil { t.Fatalf("ListenUnix on %s: %s", tmpname, err) } - defer os.Remove(tmpname) + defer func() { + ln.Close() + os.Remove(tmpname) + }() go func() { c, err := ln.Accept() @@ -214,3 +218,41 @@ func TestTCPClose(t *testing.T) { t.Fatal(err) } } + +func TestErrorNil(t *testing.T) { + c, err := Dial("tcp", "127.0.0.1:65535") + if err == nil { + t.Fatal("Dial 127.0.0.1:65535 succeeded") + } + if c != nil { + t.Fatalf("Dial returned non-nil interface %T(%v) with err != nil", c, c) + } + + // Make Listen fail by relistening on the same address. + l, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal("Listen 127.0.0.1:0: %v", err) + } + defer l.Close() + l1, err := Listen("tcp", l.Addr().String()) + if err == nil { + t.Fatal("second Listen %v: %v", l.Addr(), err) + } + if l1 != nil { + t.Fatalf("Listen returned non-nil interface %T(%v) with err != nil", l1, l1) + } + + // Make ListenPacket fail by relistening on the same address. + lp, err := ListenPacket("udp", "127.0.0.1:0") + if err != nil { + t.Fatal("Listen 127.0.0.1:0: %v", err) + } + defer lp.Close() + lp1, err := ListenPacket("udp", lp.LocalAddr().String()) + if err == nil { + t.Fatal("second Listen %v: %v", lp.LocalAddr(), err) + } + if lp1 != nil { + t.Fatalf("ListenPacket returned non-nil interface %T(%v) with err != nil", lp1, lp1) + } +} diff --git a/libgo/go/net/packetconn_test.go b/libgo/go/net/packetconn_test.go index ec5dd71..945003f 100644 --- a/libgo/go/net/packetconn_test.go +++ b/libgo/go/net/packetconn_test.go @@ -21,6 +21,45 @@ func strfunc(s string) func() string { } } +func packetConnTestData(t *testing.T, net string, i int) ([]byte, func()) { + switch net { + case "udp": + return []byte("UDP PACKETCONN TEST"), nil + case "ip": + if skip, skipmsg := skipRawSocketTest(t); skip { + return nil, func() { + t.Logf(skipmsg) + } + } + b, err := (&icmpMessage{ + Type: icmpv4EchoRequest, Code: 0, + Body: &icmpEcho{ + ID: os.Getpid() & 0xffff, Seq: i + 1, + Data: []byte("IP PACKETCONN TEST"), + }, + }).Marshal() + if err != nil { + return nil, func() { + t.Fatalf("icmpMessage.Marshal failed: %v", err) + } + } + return b, nil + case "unixgram": + switch runtime.GOOS { + case "plan9", "windows": + return nil, func() { + t.Logf("skipping %q test on %q", net, runtime.GOOS) + } + default: + return []byte("UNIXGRAM PACKETCONN TEST"), nil + } + default: + return nil, func() { + t.Logf("skipping %q test", net) + } + } +} + var packetConnTests = []struct { net string addr1 func() string @@ -42,37 +81,10 @@ func TestPacketConn(t *testing.T) { } for i, tt := range packetConnTests { - var wb []byte netstr := strings.Split(tt.net, ":") - switch netstr[0] { - case "udp": - wb = []byte("UDP PACKETCONN TEST") - case "ip": - switch runtime.GOOS { - case "plan9": - continue - } - if os.Getuid() != 0 { - continue - } - var err error - wb, err = (&icmpMessage{ - Type: icmpv4EchoRequest, Code: 0, - Body: &icmpEcho{ - ID: os.Getpid() & 0xffff, Seq: i + 1, - Data: []byte("IP PACKETCONN TEST"), - }, - }).Marshal() - if err != nil { - t.Fatalf("icmpMessage.Marshal failed: %v", err) - } - case "unixgram": - switch runtime.GOOS { - case "plan9", "windows": - continue - } - wb = []byte("UNIXGRAM PACKETCONN TEST") - default: + wb, skipOrFatalFn := packetConnTestData(t, netstr[0], i) + if skipOrFatalFn != nil { + skipOrFatalFn() continue } @@ -127,35 +139,9 @@ func TestConnAndPacketConn(t *testing.T) { for i, tt := range packetConnTests { var wb []byte netstr := strings.Split(tt.net, ":") - switch netstr[0] { - case "udp": - wb = []byte("UDP PACKETCONN TEST") - case "ip": - switch runtime.GOOS { - case "plan9": - continue - } - if os.Getuid() != 0 { - continue - } - var err error - wb, err = (&icmpMessage{ - Type: icmpv4EchoRequest, Code: 0, - Body: &icmpEcho{ - ID: os.Getpid() & 0xffff, Seq: i + 1, - Data: []byte("IP PACKETCONN TEST"), - }, - }).Marshal() - if err != nil { - t.Fatalf("icmpMessage.Marshal failed: %v", err) - } - case "unixgram": - switch runtime.GOOS { - case "plan9", "windows": - continue - } - wb = []byte("UNIXGRAM PACKETCONN TEST") - default: + wb, skipOrFatalFn := packetConnTestData(t, netstr[0], i) + if skipOrFatalFn != nil { + skipOrFatalFn() continue } @@ -186,7 +172,7 @@ func TestConnAndPacketConn(t *testing.T) { } rb1 := make([]byte, 128) if _, _, err := c1.ReadFrom(rb1); err != nil { - t.Fatalf("PacetConn.ReadFrom failed: %v", err) + t.Fatalf("PacketConn.ReadFrom failed: %v", err) } var dst Addr switch netstr[0] { diff --git a/libgo/go/net/parse_test.go b/libgo/go/net/parse_test.go index 9df0c53..b86bc32 100644 --- a/libgo/go/net/parse_test.go +++ b/libgo/go/net/parse_test.go @@ -23,12 +23,14 @@ func TestReadLine(t *testing.T) { if err != nil { t.Fatalf("open %s: %v", filename, err) } + defer fd.Close() br := bufio.NewReader(fd) file, err := open(filename) if file == nil { t.Fatalf("net.open(%s) = nil", filename) } + defer file.close() lineno := 1 byteno := 0 diff --git a/libgo/go/net/port_unix.go b/libgo/go/net/port_unix.go index 16780da..3cd9ca2 100644 --- a/libgo/go/net/port_unix.go +++ b/libgo/go/net/port_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // Read system port mappings from /etc/services diff --git a/libgo/go/net/protoconn_test.go b/libgo/go/net/protoconn_test.go index b59925e..5a8958b 100644 --- a/libgo/go/net/protoconn_test.go +++ b/libgo/go/net/protoconn_test.go @@ -103,6 +103,7 @@ func TestTCPConnSpecificMethods(t *testing.T) { } defer c.Close() c.SetKeepAlive(false) + c.SetKeepAlivePeriod(3 * time.Second) c.SetLinger(0) c.SetNoDelay(false) c.LocalAddr() @@ -160,15 +161,20 @@ func TestUDPConnSpecificMethods(t *testing.T) { } else { f.Close() } + + defer func() { + if p := recover(); p != nil { + t.Fatalf("UDPConn.WriteToUDP or WriteMsgUDP panicked: %v", p) + } + }() + + c.WriteToUDP(wb, nil) + c.WriteMsgUDP(wb, nil, nil) } func TestIPConnSpecificMethods(t *testing.T) { - switch runtime.GOOS { - case "plan9": - t.Skipf("skipping test on %q", runtime.GOOS) - } - if os.Getuid() != 0 { - t.Skipf("skipping test; must be root") + if skip, skipmsg := skipRawSocketTest(t); skip { + t.Skip(skipmsg) } la, err := ResolveIPAddr("ip4", "127.0.0.1") @@ -198,7 +204,7 @@ func TestIPConnSpecificMethods(t *testing.T) { if err != nil { t.Fatalf("icmpMessage.Marshal failed: %v", err) } - rb := make([]byte, 20+128) + rb := make([]byte, 20+len(wb)) if _, err := c.WriteToIP(wb, c.LocalAddr().(*IPAddr)); err != nil { t.Fatalf("IPConn.WriteToIP failed: %v", err) } @@ -217,6 +223,15 @@ func TestIPConnSpecificMethods(t *testing.T) { } else { f.Close() } + + defer func() { + if p := recover(); p != nil { + t.Fatalf("IPConn.WriteToIP or WriteMsgIP panicked: %v", p) + } + }() + + c.WriteToIP(wb, nil) + c.WriteMsgIP(wb, nil, nil) } func TestUnixListenerSpecificMethods(t *testing.T) { @@ -357,4 +372,15 @@ func TestUnixConnSpecificMethods(t *testing.T) { } else { f.Close() } + + defer func() { + if p := recover(); p != nil { + t.Fatalf("UnixConn.WriteToUnix or WriteMsgUnix panicked: %v", p) + } + }() + + c1.WriteToUnix(wb, nil) + c1.WriteMsgUnix(wb, nil, nil) + c3.WriteToUnix(wb, nil) + c3.WriteMsgUnix(wb, nil, nil) } diff --git a/libgo/go/net/race.go b/libgo/go/net/race.go new file mode 100644 index 0000000..2f02a6c --- /dev/null +++ b/libgo/go/net/race.go @@ -0,0 +1,31 @@ +// 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 race +// +build windows + +package net + +import ( + "runtime" + "unsafe" +) + +const raceenabled = true + +func raceAcquire(addr unsafe.Pointer) { + runtime.RaceAcquire(addr) +} + +func raceReleaseMerge(addr unsafe.Pointer) { + runtime.RaceReleaseMerge(addr) +} + +func raceReadRange(addr unsafe.Pointer, len int) { + runtime.RaceReadRange(addr, len) +} + +func raceWriteRange(addr unsafe.Pointer, len int) { + runtime.RaceWriteRange(addr, len) +} diff --git a/libgo/go/net/race0.go b/libgo/go/net/race0.go new file mode 100644 index 0000000..f504297 --- /dev/null +++ b/libgo/go/net/race0.go @@ -0,0 +1,26 @@ +// 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 !race +// +build windows + +package net + +import ( + "unsafe" +) + +const raceenabled = false + +func raceAcquire(addr unsafe.Pointer) { +} + +func raceReleaseMerge(addr unsafe.Pointer) { +} + +func raceReadRange(addr unsafe.Pointer, len int) { +} + +func raceWriteRange(addr unsafe.Pointer, len int) { +} diff --git a/libgo/go/net/rpc/client.go b/libgo/go/net/rpc/client.go index 4b0c9c3..c524d0a 100644 --- a/libgo/go/net/rpc/client.go +++ b/libgo/go/net/rpc/client.go @@ -58,6 +58,7 @@ type Client struct { // argument to force the body of the response to be read and then // discarded. type ClientCodec interface { + // WriteRequest must be safe for concurrent use by multiple goroutines. WriteRequest(*Request, interface{}) error ReadResponseHeader(*Response) error ReadResponseBody(interface{}) error @@ -160,7 +161,7 @@ func (client *Client) input() { } client.mutex.Unlock() client.sending.Unlock() - if err != io.EOF && !closing { + if debugLog && err != io.EOF && !closing { log.Println("rpc: client protocol error:", err) } } @@ -172,7 +173,9 @@ func (call *Call) done() { default: // We don't want to block here. It is the caller's responsibility to make // sure the channel has enough buffer space. See comment in Go(). - log.Println("rpc: discarding Call reply due to insufficient Done chan capacity") + if debugLog { + log.Println("rpc: discarding Call reply due to insufficient Done chan capacity") + } } } diff --git a/libgo/go/net/rpc/debug.go b/libgo/go/net/rpc/debug.go index 663663f..926466d 100644 --- a/libgo/go/net/rpc/debug.go +++ b/libgo/go/net/rpc/debug.go @@ -38,6 +38,9 @@ const debugText = `<html> var debug = template.Must(template.New("RPC debug").Parse(debugText)) +// If set, print log statements for internal and I/O errors. +var debugLog = false + type debugMethod struct { Type *methodType Name string diff --git a/libgo/go/net/rpc/jsonrpc/server.go b/libgo/go/net/rpc/jsonrpc/server.go index 5bc05fd..16ec0fe 100644 --- a/libgo/go/net/rpc/jsonrpc/server.go +++ b/libgo/go/net/rpc/jsonrpc/server.go @@ -20,8 +20,7 @@ type serverCodec struct { c io.Closer // temporary work space - req serverRequest - resp serverResponse + req serverRequest // JSON-RPC clients can use arbitrary json values as request IDs. // Package rpc expects uint64 request IDs. diff --git a/libgo/go/net/rpc/server.go b/libgo/go/net/rpc/server.go index e71b6fb..7eb2dcf 100644 --- a/libgo/go/net/rpc/server.go +++ b/libgo/go/net/rpc/server.go @@ -247,10 +247,12 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro sname = name } if sname == "" { - log.Fatal("rpc: no service name for type", s.typ.String()) + s := "rpc.Register: no service name for type " + s.typ.String() + log.Print(s) + return errors.New(s) } if !isExported(sname) && !useName { - s := "rpc Register: type " + sname + " is not exported" + s := "rpc.Register: type " + sname + " is not exported" log.Print(s) return errors.New(s) } @@ -258,13 +260,13 @@ func (server *Server) register(rcvr interface{}, name string, useName bool) erro return errors.New("rpc: service already defined: " + sname) } s.name = sname - s.method = make(map[string]*methodType) // Install the methods s.method = suitableMethods(s.typ, true) if len(s.method) == 0 { str := "" + // To help the user, see if a pointer receiver would work. method := suitableMethods(reflect.PtrTo(s.typ), false) if len(method) != 0 { @@ -356,7 +358,7 @@ func (server *Server) sendResponse(sending *sync.Mutex, req *Request, reply inte resp.Seq = req.Seq sending.Lock() err := codec.WriteResponse(resp, reply) - if err != nil { + if debugLog && err != nil { log.Println("rpc: writing response:", err) } sending.Unlock() @@ -434,7 +436,7 @@ func (server *Server) ServeCodec(codec ServerCodec) { for { service, mtype, req, argv, replyv, keepReading, err := server.readRequest(codec) if err != nil { - if err != io.EOF { + if debugLog && err != io.EOF { log.Println("rpc:", err) } if !keepReading { @@ -560,20 +562,23 @@ func (server *Server) readRequestHeader(codec ServerCodec) (service *service, mt // we can still recover and move on to the next request. keepReading = true - serviceMethod := strings.Split(req.ServiceMethod, ".") - if len(serviceMethod) != 2 { + dot := strings.LastIndex(req.ServiceMethod, ".") + if dot < 0 { err = errors.New("rpc: service/method request ill-formed: " + req.ServiceMethod) return } + serviceName := req.ServiceMethod[:dot] + methodName := req.ServiceMethod[dot+1:] + // Look up the request. server.mu.RLock() - service = server.serviceMap[serviceMethod[0]] + service = server.serviceMap[serviceName] server.mu.RUnlock() if service == nil { err = errors.New("rpc: can't find service " + req.ServiceMethod) return } - mtype = service.method[serviceMethod[1]] + mtype = service.method[methodName] if mtype == nil { err = errors.New("rpc: can't find method " + req.ServiceMethod) } @@ -612,6 +617,7 @@ func RegisterName(name string, rcvr interface{}) error { type ServerCodec interface { ReadRequestHeader(*Request) error ReadRequestBody(interface{}) error + // WriteResponse must be safe for concurrent use by multiple goroutines. WriteResponse(*Response, interface{}) error Close() error diff --git a/libgo/go/net/rpc/server_test.go b/libgo/go/net/rpc/server_test.go index eb17210..3b9a883 100644 --- a/libgo/go/net/rpc/server_test.go +++ b/libgo/go/net/rpc/server_test.go @@ -84,6 +84,7 @@ func listenTCP() (net.Listener, string) { func startServer() { Register(new(Arith)) + RegisterName("net.rpc.Arith", new(Arith)) var l net.Listener l, serverAddr = listenTCP() @@ -97,11 +98,13 @@ func startServer() { func startNewServer() { newServer = NewServer() newServer.Register(new(Arith)) + newServer.RegisterName("net.rpc.Arith", new(Arith)) + newServer.RegisterName("newServer.Arith", new(Arith)) var l net.Listener l, newServerAddr = listenTCP() log.Println("NewServer test RPC server listening on", newServerAddr) - go Accept(l) + go newServer.Accept(l) newServer.HandleHTTP(newHttpPath, "/bar") httpOnce.Do(startHttpServer) @@ -118,6 +121,7 @@ func TestRPC(t *testing.T) { testRPC(t, serverAddr) newOnce.Do(startNewServer) testRPC(t, newServerAddr) + testNewServerRPC(t, newServerAddr) } func testRPC(t *testing.T, addr string) { @@ -125,6 +129,7 @@ func testRPC(t *testing.T, addr string) { if err != nil { t.Fatal("dialing", err) } + defer client.Close() // Synchronous calls args := &Args{7, 8} @@ -233,6 +238,36 @@ func testRPC(t *testing.T, addr string) { if reply.C != args.A*args.B { t.Errorf("Mul: expected %d got %d", reply.C, args.A*args.B) } + + // ServiceName contain "." character + args = &Args{7, 8} + reply = new(Reply) + err = client.Call("net.rpc.Arith.Add", args, reply) + if err != nil { + t.Errorf("Add: expected no error but got string %q", err.Error()) + } + if reply.C != args.A+args.B { + t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) + } +} + +func testNewServerRPC(t *testing.T, addr string) { + client, err := Dial("tcp", addr) + if err != nil { + t.Fatal("dialing", err) + } + defer client.Close() + + // Synchronous calls + args := &Args{7, 8} + reply := new(Reply) + err = client.Call("newServer.Arith.Add", args, reply) + if err != nil { + t.Errorf("Add: expected no error but got string %q", err.Error()) + } + if reply.C != args.A+args.B { + t.Errorf("Add: expected %d got %d", reply.C, args.A+args.B) + } } func TestHTTP(t *testing.T) { @@ -253,6 +288,7 @@ func testHTTPRPC(t *testing.T, path string) { if err != nil { t.Fatal("dialing", err) } + defer client.Close() // Synchronous calls args := &Args{7, 8} @@ -329,6 +365,7 @@ func TestServeRequest(t *testing.T) { func testServeRequest(t *testing.T, server *Server) { client := CodecEmulator{server: server} + defer client.Close() args := &Args{7, 8} reply := new(Reply) @@ -411,6 +448,7 @@ func (WriteFailCodec) Close() error { func TestSendDeadlock(t *testing.T) { client := NewClientWithCodec(WriteFailCodec(0)) + defer client.Close() done := make(chan bool) go func() { @@ -449,6 +487,8 @@ func countMallocs(dial func() (*Client, error), t *testing.T) float64 { if err != nil { t.Fatal("error dialing", err) } + defer client.Close() + args := &Args{7, 8} reply := new(Reply) return testing.AllocsPerRun(100, func() { @@ -463,6 +503,9 @@ func countMallocs(dial func() (*Client, error), t *testing.T) float64 { } func TestCountMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -470,6 +513,9 @@ func TestCountMallocs(t *testing.T) { } func TestCountMallocsOverHTTP(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -496,6 +542,8 @@ func (writeCrasher) Write(p []byte) (int, error) { func TestClientWriteError(t *testing.T) { w := &writeCrasher{done: make(chan bool)} c := NewClient(w) + defer c.Close() + res := false err := c.Call("foo", 1, &res) if err == nil { @@ -552,6 +600,7 @@ func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) { if err != nil { b.Fatal("error dialing:", err) } + defer client.Close() // Synchronous calls args := &Args{7, 8} @@ -587,6 +636,7 @@ func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) { if err != nil { b.Fatal("error dialing:", err) } + defer client.Close() // Asynchronous calls args := &Args{7, 8} diff --git a/libgo/go/net/sendfile_dragonfly.go b/libgo/go/net/sendfile_dragonfly.go new file mode 100644 index 0000000..a2219c1 --- /dev/null +++ b/libgo/go/net/sendfile_dragonfly.go @@ -0,0 +1,103 @@ +// 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 ( + "io" + "os" + "syscall" +) + +// maxSendfileSize is the largest chunk size we ask the kernel to copy +// at a time. +const maxSendfileSize int = 4 << 20 + +// sendFile copies the contents of r to c using the sendfile +// system call to minimize copies. +// +// if handled == true, sendFile returns the number of bytes copied and any +// non-EOF error. +// +// 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 + // exactly the number of bytes told to. As such, we need to know exactly how many + // bytes to send. + var remain int64 = 0 + + lr, ok := r.(*io.LimitedReader) + if ok { + remain, r = lr.N, lr.R + if remain <= 0 { + return 0, nil, true + } + } + f, ok := r.(*os.File) + if !ok { + return 0, nil, false + } + + if remain == 0 { + fi, err := f.Stat() + if err != nil { + return 0, err, false + } + + remain = fi.Size() + } + + // The other quirk with DragonFly's sendfile implementation is that it doesn't + // use the current position of the file -- if you pass it offset 0, it starts + // from offset 0. There's no way to tell it "start from current position", so + // we have to manage that explicitly. + pos, err := f.Seek(0, os.SEEK_CUR) + if err != nil { + return 0, err, false + } + + if err := c.writeLock(); err != nil { + return 0, err, true + } + defer c.writeUnlock() + + dst := c.sysfd + src := int(f.Fd()) + for remain > 0 { + n := maxSendfileSize + if int64(n) > remain { + n = int(remain) + } + pos1 := pos + n, err1 := syscall.Sendfile(dst, src, &pos1, n) + if n > 0 { + pos += int64(n) + written += int64(n) + remain -= int64(n) + } + if n == 0 && err1 == nil { + break + } + if err1 == syscall.EAGAIN { + if err1 = c.pd.WaitWrite(); err1 == nil { + continue + } + } + if err1 == syscall.EINTR { + continue + } + if err1 != nil { + // This includes syscall.ENOSYS (no kernel + // support) and syscall.EINVAL (fd types which + // don't implement sendfile together) + err = &OpError{"sendfile", c.net, c.raddr, err1} + break + } + } + if lr != nil { + lr.N = remain + } + return written, err, written > 0 +} diff --git a/libgo/go/net/sendfile_freebsd.go b/libgo/go/net/sendfile_freebsd.go index dc5b767..42fe799 100644 --- a/libgo/go/net/sendfile_freebsd.go +++ b/libgo/go/net/sendfile_freebsd.go @@ -58,12 +58,10 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, err, false } - c.wio.Lock() - defer c.wio.Unlock() - if err := c.incref(false); err != nil { + if err := c.writeLock(); err != nil { return 0, err, true } - defer c.decref() + defer c.writeUnlock() dst := c.sysfd src := int(f.Fd()) diff --git a/libgo/go/net/sendfile_linux.go b/libgo/go/net/sendfile_linux.go index 6f1323b..5e11763 100644 --- a/libgo/go/net/sendfile_linux.go +++ b/libgo/go/net/sendfile_linux.go @@ -36,12 +36,10 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, nil, false } - c.wio.Lock() - defer c.wio.Unlock() - if err := c.incref(false); err != nil { + if err := c.writeLock(); err != nil { return 0, err, true } - defer c.decref() + defer c.writeUnlock() dst := c.sysfd src := int(f.Fd()) diff --git a/libgo/go/net/sendfile_windows.go b/libgo/go/net/sendfile_windows.go index 2d64f2f..b128ba2 100644 --- a/libgo/go/net/sendfile_windows.go +++ b/libgo/go/net/sendfile_windows.go @@ -10,20 +10,6 @@ import ( "syscall" ) -type sendfileOp struct { - anOp - src syscall.Handle // source - n uint32 -} - -func (o *sendfileOp) Submit() (err error) { - return syscall.TransmitFile(o.fd.sysfd, o.src, o.n, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) -} - -func (o *sendfileOp) Name() string { - return "TransmitFile" -} - // sendFile copies the contents of r to c using the TransmitFile // system call to minimize copies. // @@ -33,7 +19,7 @@ func (o *sendfileOp) Name() string { // if handled == false, sendFile performed no work. // // Note that sendfile for windows does not suppport >2GB file. -func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { +func sendFile(fd *netFD, r io.Reader) (written int64, err error, handled bool) { var n int64 = 0 // by default, copy until EOF lr, ok := r.(*io.LimitedReader) @@ -48,18 +34,17 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) { return 0, nil, false } - if err := c.incref(false); err != nil { + if err := fd.writeLock(); err != nil { return 0, err, true } - defer c.decref() - c.wio.Lock() - defer c.wio.Unlock() - - var o sendfileOp - o.Init(c, 'w') - o.n = uint32(n) - o.src = syscall.Handle(f.Fd()) - done, err := iosrv.ExecIO(&o, 0) + defer fd.writeUnlock() + + o := &fd.wop + o.qty = uint32(n) + o.handle = syscall.Handle(f.Fd()) + done, err := wsrv.ExecIO(o, "TransmitFile", func(o *operation) error { + return syscall.TransmitFile(o.fd.sysfd, o.handle, o.qty, 0, &o.o, nil, syscall.TF_WRITE_BEHIND) + }) if err != nil { return 0, err, false } diff --git a/libgo/go/net/singleflight.go b/libgo/go/net/singleflight.go new file mode 100644 index 0000000..dc58aff --- /dev/null +++ b/libgo/go/net/singleflight.go @@ -0,0 +1,53 @@ +// 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 "sync" + +// call is an in-flight or completed singleflight.Do call +type call struct { + wg sync.WaitGroup + val interface{} + err error + dups int +} + +// singleflight represents a class of work and forms a namespace in +// which units of work can be executed with duplicate suppression. +type singleflight struct { + mu sync.Mutex // protects m + m map[string]*call // lazily initialized +} + +// Do executes and returns the results of the given function, making +// sure that only one execution is in-flight for a given key at a +// time. If a duplicate comes in, the duplicate caller waits for the +// original to complete and receives the same results. +// The return value shared indicates whether v was given to multiple callers. +func (g *singleflight) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) { + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + if c, ok := g.m[key]; ok { + c.dups++ + g.mu.Unlock() + c.wg.Wait() + return c.val, c.err, true + } + c := new(call) + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + + c.val, c.err = fn() + c.wg.Done() + + g.mu.Lock() + delete(g.m, key) + g.mu.Unlock() + + return c.val, c.err, c.dups > 0 +} diff --git a/libgo/go/net/smtp/smtp.go b/libgo/go/net/smtp/smtp.go index 4b91778..a0a478a 100644 --- a/libgo/go/net/smtp/smtp.go +++ b/libgo/go/net/smtp/smtp.go @@ -41,12 +41,13 @@ type Client struct { } // Dial returns a new Client connected to an SMTP server at addr. +// The addr must include a port number. func Dial(addr string) (*Client, error) { conn, err := net.Dial("tcp", addr) if err != nil { return nil, err } - host := addr[:strings.Index(addr, ":")] + host, _, _ := net.SplitHostPort(addr) return NewClient(conn, host) } @@ -63,6 +64,11 @@ func NewClient(conn net.Conn, host string) (*Client, error) { return c, nil } +// Close closes the connection. +func (c *Client) Close() error { + return c.Text.Close() +} + // hello runs a hello exchange if needed. func (c *Client) hello() error { if !c.didHello { @@ -190,7 +196,9 @@ func (c *Client) Auth(a Auth) error { default: err = &textproto.Error{Code: code, Msg: msg64} } - resp, err = a.Next(msg, code == 334) + if err == nil { + resp, err = a.Next(msg, code == 334) + } if err != nil { // abort the AUTH c.cmd(501, "*") @@ -256,15 +264,17 @@ func (c *Client) Data() (io.WriteCloser, error) { return &dataCloser{c, c.Text.DotWriter()}, nil } -// SendMail connects to the server at addr, switches to TLS if possible, -// authenticates with mechanism a if possible, and then sends an email from -// address from, to addresses to, with message msg. +// 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 +// message msg. func SendMail(addr string, a Auth, from string, to []string, msg []byte) error { c, err := Dial(addr) if err != nil { return err } - if err := c.hello(); err != nil { + defer c.Close() + if err = c.hello(); err != nil { return err } if ok, _ := c.Extension("STARTTLS"); ok { diff --git a/libgo/go/net/smtp/smtp_test.go b/libgo/go/net/smtp/smtp_test.go index c190b32..2133dc7 100644 --- a/libgo/go/net/smtp/smtp_test.go +++ b/libgo/go/net/smtp/smtp_test.go @@ -238,6 +238,7 @@ func TestNewClient(t *testing.T) { if err != nil { t.Fatalf("NewClient: %v\n(after %v)", err, out()) } + defer c.Close() if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" { t.Fatalf("Expected AUTH supported") } @@ -278,6 +279,7 @@ func TestNewClient2(t *testing.T) { if err != nil { t.Fatalf("NewClient: %v", err) } + defer c.Close() if ok, _ := c.Extension("DSN"); ok { t.Fatalf("Shouldn't support DSN") } @@ -323,6 +325,7 @@ func TestHello(t *testing.T) { if err != nil { t.Fatalf("NewClient: %v", err) } + defer c.Close() c.localName = "customhost" err = nil @@ -501,3 +504,47 @@ SendMail is working for me. . QUIT ` + +func TestAuthFailed(t *testing.T) { + server := strings.Join(strings.Split(authFailedServer, "\n"), "\r\n") + client := strings.Join(strings.Split(authFailedClient, "\n"), "\r\n") + var cmdbuf bytes.Buffer + bcmdbuf := bufio.NewWriter(&cmdbuf) + var fake faker + fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(server)), bcmdbuf) + c, err := NewClient(fake, "fake.host") + if err != nil { + t.Fatalf("NewClient: %v", err) + } + defer c.Close() + + c.tls = true + c.serverName = "smtp.google.com" + err = c.Auth(PlainAuth("", "user", "pass", "smtp.google.com")) + + if err == nil { + t.Error("Auth: expected error; got none") + } else if err.Error() != "535 Invalid credentials\nplease see www.example.com" { + t.Errorf("Auth: got error: %v, want: %s", err, "535 Invalid credentials\nplease see www.example.com") + } + + bcmdbuf.Flush() + actualcmds := cmdbuf.String() + if client != actualcmds { + t.Errorf("Got:\n%s\nExpected:\n%s", actualcmds, client) + } +} + +var authFailedServer = `220 hello world +250-mx.google.com at your service +250 AUTH LOGIN PLAIN +535-Invalid credentials +535 please see www.example.com +221 Goodbye +` + +var authFailedClient = `EHLO localhost +AUTH PLAIN AHVzZXIAcGFzcw== +* +QUIT +` diff --git a/libgo/go/net/sock_bsd.go b/libgo/go/net/sock_bsd.go index d993492..6c37109 100644 --- a/libgo/go/net/sock_bsd.go +++ b/libgo/go/net/sock_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/libgo/go/net/sock_plan9.go b/libgo/go/net/sock_plan9.go new file mode 100644 index 0000000..88d9ed1 --- /dev/null +++ b/libgo/go/net/sock_plan9.go @@ -0,0 +1,10 @@ +// 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 + +func maxListenerBacklog() int { + // /sys/include/ape/sys/socket.h:/SOMAXCONN + return 5 +} diff --git a/libgo/go/net/sock_posix.go b/libgo/go/net/sock_posix.go index be89c26d..c2d343c 100644 --- a/libgo/go/net/sock_posix.go +++ b/libgo/go/net/sock_posix.go @@ -2,78 +2,197 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net import ( + "os" "syscall" "time" ) -var listenerBacklog = maxListenerBacklog() +// A sockaddr represents a TCP, UDP, IP or Unix network endpoint +// address that can be converted into a syscall.Sockaddr. +type sockaddr interface { + Addr -// Generic POSIX socket creation. -func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { - s, err := sysSocket(f, t, p) + netaddr + + // family returns the platform-dependent address family + // identifier. + family() int + + // isWildcard reports whether the address is a wildcard + // address. + isWildcard() bool + + // sockaddr returns the address converted into a syscall + // sockaddr type that implements syscall.Sockaddr + // interface. It returns a nil interface when the address is + // nil. + sockaddr(family int) (syscall.Sockaddr, error) +} + +// socket returns a network file descriptor that is ready for +// asynchronous I/O using the network poller. +func socket(net string, family, sotype, proto int, ipv6only bool, laddr, raddr sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) { + s, err := sysSocket(family, sotype, proto) if err != nil { return nil, err } - - if err = setDefaultSockopts(s, f, t, ipv6only); err != nil { + if err = setDefaultSockopts(s, family, sotype, ipv6only); err != nil { closesocket(s) return nil, err } - - // This socket is used by a listener. - if ulsa != nil && ursa == nil { - // We provide a socket that listens to a wildcard - // address with reusable UDP port when the given ulsa - // is an appropriate UDP multicast address prefix. - // This makes it possible for a single UDP listener - // to join multiple different group addresses, for - // multiple UDP listeners that listen on the same UDP - // port to join the same group address. - if ulsa, err = listenerSockaddr(s, f, ulsa, toAddr); err != nil { - closesocket(s) - return nil, err - } + if fd, err = newFD(s, family, sotype, net); err != nil { + closesocket(s) + return nil, err } - if ulsa != nil { - if err = syscall.Bind(s, ulsa); err != nil { - closesocket(s) - return nil, err + // This function makes a network file descriptor for the + // following applications: + // + // - An endpoint holder that opens a passive stream + // connenction, known as a stream listener + // + // - An endpoint holder that opens a destination-unspecific + // datagram connection, known as a datagram listener + // + // - An endpoint holder that opens an active stream or a + // destination-specific datagram connection, known as a + // dialer + // + // - An endpoint holder that opens the other connection, such + // as talking to the protocol stack inside the kernel + // + // For stream and datagram listeners, they will only require + // named sockets, so we can assume that it's just a request + // from stream or datagram listeners when laddr is not nil but + // raddr is nil. Otherwise we assume it's just for dialers or + // the other connection holders. + + if laddr != nil && raddr == nil { + switch sotype { + case syscall.SOCK_STREAM, syscall.SOCK_SEQPACKET: + if err := fd.listenStream(laddr, listenerBacklog, toAddr); err != nil { + fd.Close() + return nil, err + } + return fd, nil + case syscall.SOCK_DGRAM: + if err := fd.listenDatagram(laddr, toAddr); err != nil { + fd.Close() + return nil, err + } + return fd, nil } } - - if fd, err = newFD(s, f, t, net); err != nil { - closesocket(s) + if err := fd.dial(laddr, raddr, deadline, toAddr); err != nil { + fd.Close() return nil, err } + return fd, nil +} - // This socket is used by a dialer. - if ursa != nil { - if !deadline.IsZero() { - setWriteDeadline(fd, deadline) +func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) error { + var err error + var lsa syscall.Sockaddr + if laddr != nil { + if lsa, err = laddr.sockaddr(fd.family); err != nil { + return err + } else if lsa != nil { + if err := syscall.Bind(fd.sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) + } } - if err = fd.connect(ulsa, ursa); err != nil { - fd.Close() - return nil, err + } + if err := fd.init(); err != nil { + return err + } + var rsa syscall.Sockaddr + if raddr != nil { + if rsa, err = raddr.sockaddr(fd.family); err != nil { + return err + } else if rsa != nil { + if !deadline.IsZero() { + fd.setWriteDeadline(deadline) + } + if err := fd.connect(lsa, rsa); err != nil { + return err + } + fd.isConnected = true + if !deadline.IsZero() { + fd.setWriteDeadline(noDeadline) + } } - fd.isConnected = true - if !deadline.IsZero() { - setWriteDeadline(fd, time.Time{}) + } + lsa, _ = syscall.Getsockname(fd.sysfd) + if rsa, _ = syscall.Getpeername(fd.sysfd); rsa != nil { + fd.setAddr(toAddr(lsa), toAddr(rsa)) + } else { + fd.setAddr(toAddr(lsa), raddr) + } + return nil +} + +func (fd *netFD) listenStream(laddr sockaddr, backlog int, toAddr func(syscall.Sockaddr) Addr) error { + if err := setDefaultListenerSockopts(fd.sysfd); err != nil { + return err + } + if lsa, err := laddr.sockaddr(fd.family); err != nil { + return err + } else if lsa != nil { + if err := syscall.Bind(fd.sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) } } + if err := syscall.Listen(fd.sysfd, backlog); err != nil { + return os.NewSyscallError("listen", err) + } + if err := fd.init(); err != nil { + return err + } + lsa, _ := syscall.Getsockname(fd.sysfd) + fd.setAddr(toAddr(lsa), nil) + return nil +} - lsa, _ := syscall.Getsockname(s) - laddr := toAddr(lsa) - rsa, _ := syscall.Getpeername(s) - if rsa == nil { - rsa = ursa - } - raddr := toAddr(rsa) - fd.setAddr(laddr, raddr) - return fd, nil +func (fd *netFD) listenDatagram(laddr sockaddr, toAddr func(syscall.Sockaddr) Addr) error { + switch addr := laddr.(type) { + case *UDPAddr: + // We provide a socket that listens to a wildcard + // address with reusable UDP port when the given laddr + // is an appropriate UDP multicast address prefix. + // This makes it possible for a single UDP listener to + // join multiple different group addresses, for + // multiple UDP listeners that listen on the same UDP + // port to join the same group address. + if addr.IP != nil && addr.IP.IsMulticast() { + if err := setDefaultMulticastSockopts(fd.sysfd); err != nil { + return err + } + addr := *addr + switch fd.family { + case syscall.AF_INET: + addr.IP = IPv4zero + case syscall.AF_INET6: + addr.IP = IPv6unspecified + } + laddr = &addr + } + } + if lsa, err := laddr.sockaddr(fd.family); err != nil { + return err + } else if lsa != nil { + if err := syscall.Bind(fd.sysfd, lsa); err != nil { + return os.NewSyscallError("bind", err) + } + } + if err := fd.init(); err != nil { + return err + } + lsa, _ := syscall.Getsockname(fd.sysfd) + fd.setAddr(toAddr(lsa), nil) + return nil } diff --git a/libgo/go/net/sock_unix.go b/libgo/go/net/sock_unix.go deleted file mode 100644 index b0d6d49..0000000 --- a/libgo/go/net/sock_unix.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build darwin freebsd linux netbsd openbsd - -package net - -import "syscall" - -func listenerSockaddr(s, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) { - a := toAddr(la) - if a == nil { - return la, nil - } - switch a := a.(type) { - case *TCPAddr, *UnixAddr: - if err := setDefaultListenerSockopts(s); err != nil { - return nil, err - } - case *UDPAddr: - if a.IP.IsMulticast() { - if err := setDefaultMulticastSockopts(s); err != nil { - return nil, err - } - switch f { - case syscall.AF_INET: - a.IP = IPv4zero - case syscall.AF_INET6: - a.IP = IPv6unspecified - } - return a.sockaddr(f) - } - } - return la, nil -} diff --git a/libgo/go/net/sock_windows.go b/libgo/go/net/sock_windows.go index 41368d3..6ccde3a 100644 --- a/libgo/go/net/sock_windows.go +++ b/libgo/go/net/sock_windows.go @@ -12,33 +12,6 @@ func maxListenerBacklog() int { return syscall.SOMAXCONN } -func listenerSockaddr(s syscall.Handle, f int, la syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (syscall.Sockaddr, error) { - a := toAddr(la) - if a == nil { - return la, nil - } - switch a := a.(type) { - case *TCPAddr, *UnixAddr: - if err := setDefaultListenerSockopts(s); err != nil { - return nil, err - } - case *UDPAddr: - if a.IP.IsMulticast() { - if err := setDefaultMulticastSockopts(s); err != nil { - return nil, err - } - switch f { - case syscall.AF_INET: - a.IP = IPv4zero - case syscall.AF_INET6: - a.IP = IPv6unspecified - } - return a.sockaddr(f) - } - } - return la, nil -} - func sysSocket(f, t, p int) (syscall.Handle, error) { // See ../syscall/exec_unix.go for description of ForkLock. syscall.ForkLock.RLock() diff --git a/libgo/go/net/sockopt_bsd.go b/libgo/go/net/sockopt_bsd.go index af88814..4b9c2f9 100644 --- a/libgo/go/net/sockopt_bsd.go +++ b/libgo/go/net/sockopt_bsd.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd netbsd openbsd - -// Socket options for BSD variants +// +build darwin dragonfly freebsd netbsd openbsd package net @@ -13,40 +11,26 @@ import ( "syscall" ) -func setDefaultSockopts(s, f, t int, ipv6only bool) error { - switch f { - case syscall.AF_INET6: - if ipv6only { - syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1) - } else { - // 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, 0) - } +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. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + 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. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + 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. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - if err != nil { + if err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { return os.NewSyscallError("setsockopt", err) } // Allow reuse of recently-used ports. @@ -54,10 +38,7 @@ func setDefaultMulticastSockopts(s int) error { // to make an effective multicast application that requires // quick draw possible. if syscall.SO_REUSEPORT != 0 { - err = syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEPORT, 1)) } return nil } diff --git a/libgo/go/net/sockopt_linux.go b/libgo/go/net/sockopt_linux.go index 0f47538..54c20b1 100644 --- a/libgo/go/net/sockopt_linux.go +++ b/libgo/go/net/sockopt_linux.go @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Socket options for Linux - package net import ( @@ -11,41 +9,24 @@ import ( "syscall" ) -func setDefaultSockopts(s, f, t int, ipv6only bool) error { - switch f { - case syscall.AF_INET6: - if ipv6only { - syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1) - } else { - // 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, 0) - } +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. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + 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. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + 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. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)) } diff --git a/libgo/go/net/sockopt_posix.go b/libgo/go/net/sockopt_posix.go index 1590f4e..ff3bc68 100644 --- a/libgo/go/net/sockopt_posix.go +++ b/libgo/go/net/sockopt_posix.go @@ -2,9 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin freebsd linux netbsd openbsd windows - -// Socket options +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -103,7 +101,7 @@ done: } func setReadBuffer(fd *netFD, bytes int) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() @@ -111,7 +109,7 @@ func setReadBuffer(fd *netFD, bytes int) error { } func setWriteBuffer(fd *netFD, bytes int) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() @@ -119,21 +117,13 @@ func setWriteBuffer(fd *netFD, bytes int) error { } func setKeepAlive(fd *netFD, keepalive bool) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive))) } -func setNoDelay(fd *netFD, noDelay bool) error { - if err := fd.incref(false); err != nil { - return err - } - defer fd.decref() - return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))) -} - func setLinger(fd *netFD, sec int) error { var l syscall.Linger if sec >= 0 { @@ -143,7 +133,7 @@ func setLinger(fd *netFD, sec int) error { l.Onoff = 0 l.Linger = 0 } - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() diff --git a/libgo/go/net/sockopt_windows.go b/libgo/go/net/sockopt_windows.go index 0861fe8..cb64a40 100644 --- a/libgo/go/net/sockopt_windows.go +++ b/libgo/go/net/sockopt_windows.go @@ -2,27 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Socket options for Windows - package net import ( "os" "syscall" - "time" ) -func setDefaultSockopts(s syscall.Handle, f, t int, ipv6only bool) error { - switch f { - case syscall.AF_INET6: - if ipv6only { - syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 1) - } else { - // 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, 0) - } +func setDefaultSockopts(s syscall.Handle, 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. syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1) @@ -42,27 +34,5 @@ func setDefaultListenerSockopts(s syscall.Handle) error { func setDefaultMulticastSockopts(s syscall.Handle) error { // Allow multicast UDP and raw IP datagram sockets to listen // concurrently across multiple listeners. - err := syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil -} - -// TODO(dfc) these unused error returns could be removed - -func setReadDeadline(fd *netFD, t time.Time) error { - fd.rdeadline.setTime(t) - return nil -} - -func setWriteDeadline(fd *netFD, t time.Time) error { - fd.wdeadline.setTime(t) - return nil -} - -func setDeadline(fd *netFD, t time.Time) error { - setReadDeadline(fd, t) - setWriteDeadline(fd, t) - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)) } diff --git a/libgo/go/net/sockoptip_bsd.go b/libgo/go/net/sockoptip_bsd.go index 263f855..2199e48 100644 --- a/libgo/go/net/sockoptip_bsd.go +++ b/libgo/go/net/sockoptip_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package net @@ -18,25 +18,17 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } var a [4]byte copy(a[:], ip.To4()) - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err = syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptInet4Addr(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, a)) } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v))) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptByte(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, byte(boolint(v)))) } diff --git a/libgo/go/net/sockoptip_linux.go b/libgo/go/net/sockoptip_linux.go index 225fb0c..a69b778 100644 --- a/libgo/go/net/sockoptip_linux.go +++ b/libgo/go/net/sockoptip_linux.go @@ -15,25 +15,17 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { v = int32(ifi.Index) } mreq := &syscall.IPMreqn{Ifindex: v} - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreqn(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, mreq)) } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))) } diff --git a/libgo/go/net/sockoptip_posix.go b/libgo/go/net/sockoptip_posix.go index e4c56a0..c2579be 100644 --- a/libgo/go/net/sockoptip_posix.go +++ b/libgo/go/net/sockoptip_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -16,15 +16,11 @@ func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error { if err := setIPv4MreqToInterface(mreq, ifi); err != nil { return err } - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptIPMreq(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_ADD_MEMBERSHIP, mreq)) } func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { @@ -32,27 +28,19 @@ func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error { if ifi != nil { v = ifi.Index } - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_IF, v)) } func setIPv6MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v)) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_MULTICAST_LOOP, boolint(v))) } func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { @@ -61,13 +49,9 @@ func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error { if ifi != nil { mreq.Interface = uint32(ifi.Index) } - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err := syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptIPv6Mreq(fd.sysfd, syscall.IPPROTO_IPV6, syscall.IPV6_JOIN_GROUP, mreq)) } diff --git a/libgo/go/net/sockoptip_windows.go b/libgo/go/net/sockoptip_windows.go index 3e24844..7b11f20 100644 --- a/libgo/go/net/sockoptip_windows.go +++ b/libgo/go/net/sockoptip_windows.go @@ -17,26 +17,17 @@ func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error { } var a [4]byte copy(a[:], ip.To4()) - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - err = syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_IF), (*byte)(unsafe.Pointer(&a[0])), 4) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_IF, (*byte)(unsafe.Pointer(&a[0])), 4)) } func setIPv4MulticastLoopback(fd *netFD, v bool) error { - if err := fd.incref(false); err != nil { + if err := fd.incref(); err != nil { return err } defer fd.decref() - vv := int32(boolint(v)) - err := syscall.Setsockopt(fd.sysfd, int32(syscall.IPPROTO_IP), int32(syscall.IP_MULTICAST_LOOP), (*byte)(unsafe.Pointer(&vv)), 4) - if err != nil { - return os.NewSyscallError("setsockopt", err) - } - return nil + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v))) } diff --git a/libgo/go/net/sys_cloexec.go b/libgo/go/net/sys_cloexec.go index 17e8749..bbfcc1a 100644 --- a/libgo/go/net/sys_cloexec.go +++ b/libgo/go/net/sys_cloexec.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package net diff --git a/libgo/go/net/tcp_test.go b/libgo/go/net/tcp_test.go index a71b02b..62fd99f 100644 --- a/libgo/go/net/tcp_test.go +++ b/libgo/go/net/tcp_test.go @@ -6,8 +6,10 @@ package net import ( "fmt" + "io" "reflect" "runtime" + "sync" "testing" "time" ) @@ -59,7 +61,7 @@ func BenchmarkTCP6PersistentTimeout(b *testing.B) { func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) { const msgLen = 512 conns := b.N - numConcurrent := runtime.GOMAXPROCS(-1) * 16 + numConcurrent := runtime.GOMAXPROCS(-1) * 2 msgs := 1 if persistent { conns = numConcurrent @@ -147,11 +149,134 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) { } } +func BenchmarkTCP4ConcurrentReadWrite(b *testing.B) { + benchmarkTCPConcurrentReadWrite(b, "127.0.0.1:0") +} + +func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) { + if !supportsIPv6 { + b.Skip("ipv6 is not supported") + } + benchmarkTCPConcurrentReadWrite(b, "[::1]:0") +} + +func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) { + // The benchmark creates GOMAXPROCS client/server pairs. + // Each pair creates 4 goroutines: client reader/writer and server reader/writer. + // The benchmark stresses concurrent reading and writing to the same connection. + // Such pattern is used in net/http and net/rpc. + + b.StopTimer() + + P := runtime.GOMAXPROCS(0) + N := b.N / P + W := 1000 + + // Setup P client/server connections. + clients := make([]Conn, P) + servers := make([]Conn, P) + ln, err := Listen("tcp", laddr) + if err != nil { + b.Fatalf("Listen failed: %v", err) + } + defer ln.Close() + done := make(chan bool) + go func() { + for p := 0; p < P; p++ { + s, err := ln.Accept() + if err != nil { + b.Fatalf("Accept failed: %v", err) + } + servers[p] = s + } + done <- true + }() + for p := 0; p < P; p++ { + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + b.Fatalf("Dial failed: %v", err) + } + clients[p] = c + } + <-done + + b.StartTimer() + + var wg sync.WaitGroup + wg.Add(4 * P) + for p := 0; p < P; p++ { + // Client writer. + go func(c Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + v := byte(i) + for w := 0; w < W; w++ { + v *= v + } + buf[0] = v + _, err := c.Write(buf[:]) + if err != nil { + b.Fatalf("Write failed: %v", err) + } + } + }(clients[p]) + + // Pipe between server reader and server writer. + pipe := make(chan byte, 128) + + // Server reader. + go func(s Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + _, err := s.Read(buf[:]) + if err != nil { + b.Fatalf("Read failed: %v", err) + } + pipe <- buf[0] + } + }(servers[p]) + + // Server writer. + go func(s Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + v := <-pipe + for w := 0; w < W; w++ { + v *= v + } + buf[0] = v + _, err := s.Write(buf[:]) + if err != nil { + b.Fatalf("Write failed: %v", err) + } + } + s.Close() + }(servers[p]) + + // Client reader. + go func(c Conn) { + defer wg.Done() + var buf [1]byte + for i := 0; i < N; i++ { + _, err := c.Read(buf[:]) + if err != nil { + b.Fatalf("Read failed: %v", err) + } + } + c.Close() + }(clients[p]) + } + wg.Wait() +} + type resolveTCPAddrTest struct { - net string - litAddr string - addr *TCPAddr - err error + net string + litAddrOrName string + addr *TCPAddr + err error } var resolveTCPAddrTests = []resolveTCPAddrTest{ @@ -167,6 +292,8 @@ var resolveTCPAddrTests = []resolveTCPAddrTest{ {"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior {"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior + {"tcp", ":12345", &TCPAddr{Port: 12345}, nil}, + {"http", "127.0.0.1:0", nil, UnknownNetworkError("http")}, } @@ -178,16 +305,33 @@ func init() { {"tcp6", "[fe80::1%" + index + "]:4", &TCPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil}, }...) } + if ips, err := LookupIP("localhost"); err == nil && len(ips) > 1 && supportsIPv4 && supportsIPv6 { + resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{ + {"tcp", "localhost:5", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 5}, nil}, + {"tcp4", "localhost:6", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 6}, nil}, + {"tcp6", "localhost:7", &TCPAddr{IP: IPv6loopback, Port: 7}, nil}, + }...) + } } func TestResolveTCPAddr(t *testing.T) { for _, tt := range resolveTCPAddrTests { - addr, err := ResolveTCPAddr(tt.net, tt.litAddr) + addr, err := ResolveTCPAddr(tt.net, tt.litAddrOrName) if err != tt.err { - t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + t.Fatalf("ResolveTCPAddr(%q, %q) failed: %v", tt.net, tt.litAddrOrName, err) } if !reflect.DeepEqual(addr, tt.addr) { - t.Fatalf("got %#v; expected %#v", addr, tt.addr) + t.Fatalf("ResolveTCPAddr(%q, %q) = %#v, want %#v", tt.net, tt.litAddrOrName, addr, tt.addr) + } + if err == nil { + str := addr.String() + addr1, err := ResolveTCPAddr(tt.net, str) + if err != nil { + t.Fatalf("ResolveTCPAddr(%q, %q) [from %q]: %v", tt.net, str, tt.litAddrOrName, err) + } + if !reflect.DeepEqual(addr1, addr) { + t.Fatalf("ResolveTCPAddr(%q, %q) [from %q] = %#v, want %#v", tt.net, str, tt.litAddrOrName, addr1, addr) + } } } } @@ -294,3 +438,153 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) { <-done } } + +func TestTCPConcurrentAccept(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) + ln, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Listen failed: %v", err) + } + const N = 10 + var wg sync.WaitGroup + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + for { + c, err := ln.Accept() + if err != nil { + break + } + c.Close() + } + wg.Done() + }() + } + for i := 0; i < 10*N; i++ { + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial failed: %v", err) + } + c.Close() + } + ln.Close() + wg.Wait() +} + +func TestTCPReadWriteMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } + ln, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Listen failed: %v", err) + } + defer ln.Close() + var server Conn + errc := make(chan error) + go func() { + var err error + server, err = ln.Accept() + errc <- err + }() + client, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial failed: %v", err) + } + if err := <-errc; err != nil { + t.Fatalf("Accept failed: %v", err) + } + defer server.Close() + var buf [128]byte + mallocs := testing.AllocsPerRun(1000, func() { + _, err := server.Write(buf[:]) + if err != nil { + t.Fatalf("Write failed: %v", err) + } + _, err = io.ReadFull(client, buf[:]) + if err != nil { + t.Fatalf("Read failed: %v", err) + } + }) + if mallocs > 0 { + t.Fatalf("Got %v allocs, want 0", mallocs) + } +} + +func TestTCPStress(t *testing.T) { + const conns = 2 + const msgLen = 512 + msgs := int(1e4) + if testing.Short() { + msgs = 1e2 + } + + sendMsg := func(c Conn, buf []byte) bool { + n, err := c.Write(buf) + if n != len(buf) || err != nil { + t.Logf("Write failed: %v", err) + return false + } + return true + } + recvMsg := func(c Conn, buf []byte) bool { + for read := 0; read != len(buf); { + n, err := c.Read(buf) + read += n + if err != nil { + t.Logf("Read failed: %v", err) + return false + } + } + return true + } + + ln, err := Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Listen failed: %v", err) + } + defer ln.Close() + // Acceptor. + go func() { + for { + c, err := ln.Accept() + if err != nil { + break + } + // Server connection. + go func(c Conn) { + defer c.Close() + var buf [msgLen]byte + for m := 0; m < msgs; m++ { + if !recvMsg(c, buf[:]) || !sendMsg(c, buf[:]) { + break + } + } + }(c) + } + }() + done := make(chan bool) + for i := 0; i < conns; i++ { + // Client connection. + go func() { + defer func() { + done <- true + }() + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Logf("Dial failed: %v", err) + return + } + defer c.Close() + var buf [msgLen]byte + for m := 0; m < msgs; m++ { + if !sendMsg(c, buf[:]) || !recvMsg(c, buf[:]) { + break + } + } + }() + } + for i := 0; i < conns; i++ { + <-done + } +} diff --git a/libgo/go/net/tcpsock.go b/libgo/go/net/tcpsock.go index 4d9ebd2..f3dfbd2 100644 --- a/libgo/go/net/tcpsock.go +++ b/libgo/go/net/tcpsock.go @@ -18,10 +18,18 @@ func (a *TCPAddr) String() string { if a == nil { return "<nil>" } + ip := ipEmptyString(a.IP) if a.Zone != "" { - return JoinHostPort(a.IP.String()+"%"+a.Zone, itoa(a.Port)) + return JoinHostPort(ip+"%"+a.Zone, itoa(a.Port)) } - return JoinHostPort(a.IP.String(), itoa(a.Port)) + return JoinHostPort(ip, itoa(a.Port)) +} + +func (a *TCPAddr) toAddr() Addr { + if a == nil { + return nil + } + return a } // ResolveTCPAddr parses addr as a TCP address of the form "host:port" @@ -42,5 +50,5 @@ func ResolveTCPAddr(net, addr string) (*TCPAddr, error) { if err != nil { return nil, err } - return a.(*TCPAddr), nil + return a.toAddr().(*TCPAddr), nil } diff --git a/libgo/go/net/tcpsock_plan9.go b/libgo/go/net/tcpsock_plan9.go index 48334fed..cf9c0f8 100644 --- a/libgo/go/net/tcpsock_plan9.go +++ b/libgo/go/net/tcpsock_plan9.go @@ -65,6 +65,11 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error { return syscall.EPLAN9 } +// SetKeepAlivePeriod sets period between keep alives. +func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error { + return syscall.EPLAN9 +} + // SetNoDelay controls whether the operating system should delay // packet transmission in hopes of sending fewer packets (Nagle's // algorithm). The default is true (no delay), meaning that data is @@ -106,7 +111,7 @@ type TCPListener struct { } // AcceptTCP accepts the next incoming call and returns the new -// connection and the remote address. +// connection. func (l *TCPListener) AcceptTCP() (*TCPConn, error) { if l == nil || l.fd == nil || l.fd.ctl == nil { return nil, syscall.EINVAL @@ -153,7 +158,7 @@ func (l *TCPListener) SetDeadline(t time.Time) error { if l == nil || l.fd == nil || l.fd.ctl == nil { return syscall.EINVAL } - return setDeadline(l.fd, t) + return l.fd.setDeadline(t) } // File returns a copy of the underlying os.File, set to blocking diff --git a/libgo/go/net/tcpsock_posix.go b/libgo/go/net/tcpsock_posix.go index 876edb1..00c692e 100644 --- a/libgo/go/net/tcpsock_posix.go +++ b/libgo/go/net/tcpsock_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -46,14 +46,10 @@ func (a *TCPAddr) isWildcard() bool { } func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, a.Port, a.Zone) -} - -func (a *TCPAddr) toAddr() sockaddr { - if a == nil { // nil *TCPAddr - return nil // nil interface + if a == nil { + return nil, nil } - return a + return ipToSockaddr(family, a.IP, a.Port, a.Zone) } // TCPConn is an implementation of the Conn interface for TCP network @@ -121,6 +117,14 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error { return setKeepAlive(c.fd, keepalive) } +// SetKeepAlivePeriod sets period between keep alives. +func (c *TCPConn) SetKeepAlivePeriod(d time.Duration) error { + if !c.ok() { + return syscall.EINVAL + } + return setKeepAlivePeriod(c.fd, d) +} + // SetNoDelay controls whether the operating system should delay // packet transmission in hopes of sending fewer packets (Nagle's // algorithm). The default is true (no delay), meaning that data is @@ -139,16 +143,16 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) { switch net { case "tcp", "tcp4", "tcp6": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)} } if raddr == nil { - return nil, &OpError{"dial", net, nil, errMissingAddress} + return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: errMissingAddress} } return dialTCP(net, laddr, raddr, noDeadline) } func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) { - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) // TCP has a rarely used mechanism called a 'simultaneous connection' in // which Dial("tcp", addr1, addr2) run on the machine at addr1 can @@ -178,11 +182,11 @@ func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, e if err == nil { fd.Close() } - fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) + fd, err = internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP) } if err != nil { - return nil, err + return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err} } return newTCPConn(fd), nil } @@ -221,7 +225,7 @@ type TCPListener struct { } // AcceptTCP accepts the next incoming call and returns the new -// connection and the remote address. +// connection. func (l *TCPListener) AcceptTCP() (*TCPConn, error) { if l == nil || l.fd == nil { return nil, syscall.EINVAL @@ -261,7 +265,7 @@ func (l *TCPListener) SetDeadline(t time.Time) error { if l == nil || l.fd == nil { return syscall.EINVAL } - return setDeadline(l.fd, t) + return l.fd.setDeadline(t) } // File returns a copy of the underlying os.File, set to blocking @@ -281,19 +285,14 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) { switch net { case "tcp", "tcp4", "tcp6": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)} } if laddr == nil { laddr = &TCPAddr{} } - fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) - if err != nil { - return nil, err - } - err = syscall.Listen(fd.sysfd, listenerBacklog) + fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP) if err != nil { - fd.Close() - return nil, &OpError{"listen", net, laddr, err} + return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err} } return &TCPListener{fd}, nil } diff --git a/libgo/go/net/tcpsockopt_darwin.go b/libgo/go/net/tcpsockopt_darwin.go new file mode 100644 index 0000000..3314084 --- /dev/null +++ b/libgo/go/net/tcpsockopt_darwin.go @@ -0,0 +1,27 @@ +// 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. + +// TCP socket options for darwin + +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.TCP_KEEPALIVE, secs)) +} diff --git a/libgo/go/net/tcpsockopt_openbsd.go b/libgo/go/net/tcpsockopt_openbsd.go new file mode 100644 index 0000000..3480f93 --- /dev/null +++ b/libgo/go/net/tcpsockopt_openbsd.go @@ -0,0 +1,27 @@ +// 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. + +// TCP socket options for openbsd + +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)) +} diff --git a/libgo/go/net/tcpsockopt_posix.go b/libgo/go/net/tcpsockopt_posix.go new file mode 100644 index 0000000..e03476a --- /dev/null +++ b/libgo/go/net/tcpsockopt_posix.go @@ -0,0 +1,20 @@ +// 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 darwin dragonfly freebsd linux netbsd openbsd windows + +package net + +import ( + "os" + "syscall" +) + +func setNoDelay(fd *netFD, noDelay bool) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_NODELAY, boolint(noDelay))) +} diff --git a/libgo/go/net/tcpsockopt_unix.go b/libgo/go/net/tcpsockopt_unix.go new file mode 100644 index 0000000..89d9143 --- /dev/null +++ b/libgo/go/net/tcpsockopt_unix.go @@ -0,0 +1,31 @@ +// 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 dragonfly freebsd linux netbsd + +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()) + + err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, secs)) + if err != nil { + return err + } + return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, secs)) +} diff --git a/libgo/go/net/tcpsockopt_windows.go b/libgo/go/net/tcpsockopt_windows.go new file mode 100644 index 0000000..0bf4312 --- /dev/null +++ b/libgo/go/net/tcpsockopt_windows.go @@ -0,0 +1,21 @@ +// 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. + +// TCP socket options for windows + +package net + +import ( + "time" +) + +func setKeepAlivePeriod(fd *netFD, d time.Duration) error { + if err := fd.incref(); err != nil { + return err + } + defer fd.decref() + + // We can't actually set this per connection. Act as a noop rather than an error. + return nil +} diff --git a/libgo/go/net/textproto/reader.go b/libgo/go/net/textproto/reader.go index 5bd26ac..56ece5b 100644 --- a/libgo/go/net/textproto/reader.go +++ b/libgo/go/net/textproto/reader.go @@ -203,7 +203,7 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa // ReadCodeLine reads a response code line of the form // code message -// where code is a 3-digit status code and the message +// where code is a three-digit status code and the message // extends to the rest of the line. An example of such a line is: // 220 plan9.bell-labs.com ESMTP // @@ -231,7 +231,7 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err // ... // code message line n // -// where code is a 3-digit status code. The first line starts with the +// where code is a three-digit status code. The first line starts with the // code and a hyphen. The response is terminated by a line that starts // with the same code followed by a space. Each line in message is // separated by a newline (\n). @@ -456,7 +456,16 @@ func (r *Reader) ReadDotLines() ([]string, error) { // } // func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { - m := make(MIMEHeader, 4) + // Avoid lots of small slice allocations later by allocating one + // large one ahead of time which we'll cut up into smaller + // slices. If this isn't big enough later, we allocate small ones. + var strs []string + hint := r.upcomingHeaderNewlines() + if hint > 0 { + strs = make([]string, hint) + } + + m := make(MIMEHeader, hint) for { kv, err := r.readContinuedLineSlice() if len(kv) == 0 { @@ -483,7 +492,18 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { } value := string(kv[i:]) - m[key] = append(m[key], value) + vv := m[key] + if vv == nil && len(strs) > 0 { + // More than likely this will be a single-element key. + // Most headers aren't multi-valued. + // Set the capacity on strs[0] to 1, so any future append + // won't extend the slice into the other strings. + vv, strs = strs[:1:1], strs[1:] + vv[0] = value + m[key] = vv + } else { + m[key] = append(vv, value) + } if err != nil { return m, err @@ -491,6 +511,29 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { } } +// upcomingHeaderNewlines returns an approximation of the number of newlines +// that will be in this header. If it gets confused, it returns 0. +func (r *Reader) upcomingHeaderNewlines() (n int) { + // Try to determine the 'hint' size. + r.R.Peek(1) // force a buffer load if empty + s := r.R.Buffered() + if s == 0 { + return + } + peek, _ := r.R.Peek(s) + for len(peek) > 0 { + i := bytes.IndexByte(peek, '\n') + if i < 3 { + // Not present (-1) or found within the next few bytes, + // implying we're at the end ("\r\n\r\n" or "\n\n") + return + } + n++ + peek = peek[i+1:] + } + return +} + // CanonicalMIMEHeaderKey returns the canonical format of the // MIME header key s. The canonicalization converts the first // letter and any letter following a hyphen to upper case; diff --git a/libgo/go/net/textproto/textproto.go b/libgo/go/net/textproto/textproto.go index eb6ced1..026eb02 100644 --- a/libgo/go/net/textproto/textproto.go +++ b/libgo/go/net/textproto/textproto.go @@ -105,7 +105,7 @@ func Dial(network, addr string) (*Conn, error) { // if _, _, err = c.ReadCodeLine(110); err != nil { // return nil, err // } -// text, err := c.ReadDotAll() +// text, err := c.ReadDotBytes() // if err != nil { // return nil, err // } diff --git a/libgo/go/net/timeout_test.go b/libgo/go/net/timeout_test.go index 2e92147..35d427a 100644 --- a/libgo/go/net/timeout_test.go +++ b/libgo/go/net/timeout_test.go @@ -325,9 +325,6 @@ func TestReadWriteDeadline(t *testing.T) { t.Skipf("skipping test on %q", runtime.GOOS) } - if !canCancelIO { - t.Skip("skipping test on this system") - } const ( readTimeout = 50 * time.Millisecond writeTimeout = 250 * time.Millisecond @@ -496,7 +493,10 @@ func testVariousDeadlines(t *testing.T, maxProcs int) { clientc <- copyRes{n, err, d} }() - const tooLong = 2000 * time.Millisecond + tooLong := 2 * time.Second + if runtime.GOOS == "windows" { + tooLong = 5 * time.Second + } select { case res := <-clientc: if isTimeout(res.err) { @@ -549,7 +549,7 @@ func TestReadDeadlineDataAvailable(t *testing.T) { } defer c.Close() if res := <-servec; res.err != nil || res.n != int64(len(msg)) { - t.Fatalf("unexpected server Write: n=%d, err=%d; want n=%d, err=nil", res.n, res.err, len(msg)) + t.Fatalf("unexpected server Write: n=%d, err=%v; want n=%d, err=nil", res.n, res.err, len(msg)) } c.SetReadDeadline(time.Now().Add(-5 * time.Second)) // in the psat. buf := make([]byte, len(msg)/2) @@ -703,3 +703,40 @@ func TestProlongTimeout(t *testing.T) { c.Write(buf[:]) } } + +func TestDeadlineRace(t *testing.T) { + switch runtime.GOOS { + case "plan9": + t.Skipf("skipping test on %q", runtime.GOOS) + } + + N := 1000 + if testing.Short() { + N = 50 + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) + ln := newLocalListener(t) + defer ln.Close() + c, err := Dial("tcp", ln.Addr().String()) + if err != nil { + t.Fatalf("Dial: %v", err) + } + defer c.Close() + done := make(chan bool) + go func() { + t := time.NewTicker(2 * time.Microsecond).C + for i := 0; i < N; i++ { + if err := c.SetDeadline(time.Now().Add(2 * time.Microsecond)); err != nil { + break + } + <-t + } + done <- true + }() + var buf [1]byte + for i := 0; i < N; i++ { + c.Read(buf[:]) // ignore possible timeout errors + } + c.Close() + <-done +} diff --git a/libgo/go/net/udp_test.go b/libgo/go/net/udp_test.go index 4278f6d..6f4d215 100644 --- a/libgo/go/net/udp_test.go +++ b/libgo/go/net/udp_test.go @@ -5,53 +5,31 @@ package net import ( - "fmt" "reflect" "runtime" + "strings" "testing" ) -type resolveUDPAddrTest struct { - net string - litAddr string - addr *UDPAddr - err error -} - -var resolveUDPAddrTests = []resolveUDPAddrTest{ - {"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, - {"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil}, - - {"udp", "[::1]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1}, nil}, - {"udp6", "[::1]:65534", &UDPAddr{IP: ParseIP("::1"), Port: 65534}, nil}, - - {"udp", "[::1%en0]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1, Zone: "en0"}, nil}, - {"udp6", "[::1%911]:2", &UDPAddr{IP: ParseIP("::1"), Port: 2, Zone: "911"}, nil}, - - {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior - {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil}, // Go 1.0 behavior - - {"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")}, -} - -func init() { - if ifi := loopbackInterface(); ifi != nil { - index := fmt.Sprintf("%v", ifi.Index) - resolveUDPAddrTests = append(resolveUDPAddrTests, []resolveUDPAddrTest{ - {"udp6", "[fe80::1%" + ifi.Name + "]:3", &UDPAddr{IP: ParseIP("fe80::1"), Port: 3, Zone: zoneToString(ifi.Index)}, nil}, - {"udp6", "[fe80::1%" + index + "]:4", &UDPAddr{IP: ParseIP("fe80::1"), Port: 4, Zone: index}, nil}, - }...) - } -} - func TestResolveUDPAddr(t *testing.T) { - for _, tt := range resolveUDPAddrTests { - addr, err := ResolveUDPAddr(tt.net, tt.litAddr) + for _, tt := range resolveTCPAddrTests { + net := strings.Replace(tt.net, "tcp", "udp", -1) + addr, err := ResolveUDPAddr(net, tt.litAddrOrName) if err != tt.err { - t.Fatalf("ResolveUDPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err) + t.Fatalf("ResolveUDPAddr(%q, %q) failed: %v", net, tt.litAddrOrName, err) } - if !reflect.DeepEqual(addr, tt.addr) { - t.Fatalf("got %#v; expected %#v", addr, tt.addr) + if !reflect.DeepEqual(addr, (*UDPAddr)(tt.addr)) { + t.Fatalf("ResolveUDPAddr(%q, %q) = %#v, want %#v", net, tt.litAddrOrName, addr, tt.addr) + } + if err == nil { + str := addr.String() + addr1, err := ResolveUDPAddr(net, str) + if err != nil { + t.Fatalf("ResolveUDPAddr(%q, %q) [from %q]: %v", net, str, tt.litAddrOrName, err) + } + if !reflect.DeepEqual(addr1, addr) { + t.Fatalf("ResolveUDPAddr(%q, %q) [from %q] = %#v, want %#v", net, str, tt.litAddrOrName, addr1, addr) + } } } } @@ -224,7 +202,7 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) { {"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false}, } switch runtime.GOOS { - case "darwin", "freebsd", "openbsd", "netbsd": + case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd": tests = append(tests, []test{ {"udp", "[localhost%" + ifi.Name + "]:0", true}, {"udp6", "[localhost%" + ifi.Name + "]:0", true}, diff --git a/libgo/go/net/udpsock.go b/libgo/go/net/udpsock.go index 5ce7d6b..0dd0dbd 100644 --- a/libgo/go/net/udpsock.go +++ b/libgo/go/net/udpsock.go @@ -22,10 +22,18 @@ func (a *UDPAddr) String() string { if a == nil { return "<nil>" } + ip := ipEmptyString(a.IP) if a.Zone != "" { - return JoinHostPort(a.IP.String()+"%"+a.Zone, itoa(a.Port)) + return JoinHostPort(ip+"%"+a.Zone, itoa(a.Port)) } - return JoinHostPort(a.IP.String(), itoa(a.Port)) + return JoinHostPort(ip, itoa(a.Port)) +} + +func (a *UDPAddr) toAddr() Addr { + if a == nil { + return nil + } + return a } // ResolveUDPAddr parses addr as a UDP address of the form "host:port" @@ -46,5 +54,5 @@ func ResolveUDPAddr(net, addr string) (*UDPAddr, error) { if err != nil { return nil, err } - return a.(*UDPAddr), nil + return a.toAddr().(*UDPAddr), nil } diff --git a/libgo/go/net/udpsock_plan9.go b/libgo/go/net/udpsock_plan9.go index 12a3483..7362170 100644 --- a/libgo/go/net/udpsock_plan9.go +++ b/libgo/go/net/udpsock_plan9.go @@ -73,6 +73,9 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { if !c.ok() || c.fd.data == nil { return 0, syscall.EINVAL } + if addr == nil { + return 0, &OpError{Op: "write", Net: c.fd.dir, Addr: nil, Err: errMissingAddress} + } h := new(udpHeader) h.raddr = addr.IP.To16() h.laddr = c.fd.laddr.(*UDPAddr).IP.To16() diff --git a/libgo/go/net/udpsock_posix.go b/libgo/go/net/udpsock_posix.go index b90cb03..142da81 100644 --- a/libgo/go/net/udpsock_posix.go +++ b/libgo/go/net/udpsock_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -39,14 +39,10 @@ func (a *UDPAddr) isWildcard() bool { } func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) { - return ipToSockaddr(family, a.IP, a.Port, a.Zone) -} - -func (a *UDPAddr) toAddr() sockaddr { - if a == nil { // nil *UDPAddr - return nil // nil interface + if a == nil { + return nil, nil } - return a + return ipToSockaddr(family, a.IP, a.Port, a.Zone) } // UDPConn is the implementation of the Conn and PacketConn interfaces @@ -121,6 +117,9 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) { if c.fd.isConnected { return 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected} } + if addr == nil { + return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress} + } sa, err := addr.sockaddr(c.fd.family) if err != nil { return 0, &OpError{"write", c.fd.net, addr, err} @@ -150,6 +149,9 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er if c.fd.isConnected { return 0, 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected} } + if addr == nil { + return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress} + } sa, err := addr.sockaddr(c.fd.family) if err != nil { return 0, 0, &OpError{"write", c.fd.net, addr, err} @@ -161,21 +163,21 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er // which must be "udp", "udp4", or "udp6". If laddr is not nil, it is // used as the local address for the connection. func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) { - return dialUDP(net, laddr, raddr, noDeadline) -} - -func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) { switch net { case "udp", "udp4", "udp6": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)} } if raddr == nil { - return nil, &OpError{"dial", net, nil, errMissingAddress} + return nil, &OpError{Op: "dial", Net: net, Addr: nil, Err: errMissingAddress} } - fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) + return dialUDP(net, laddr, raddr, noDeadline) +} + +func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) { + fd, err := internetSocket(net, laddr, raddr, deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP) if err != nil { - return nil, err + return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err} } return newUDPConn(fd), nil } @@ -191,14 +193,14 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) { switch net { case "udp", "udp4", "udp6": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)} } if laddr == nil { laddr = &UDPAddr{} } - fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + fd, err := internetSocket(net, laddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) if err != nil { - return nil, err + return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err} } return newUDPConn(fd), nil } @@ -211,25 +213,25 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e switch net { case "udp", "udp4", "udp6": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "listen", Net: net, Addr: gaddr, Err: UnknownNetworkError(net)} } if gaddr == nil || gaddr.IP == nil { - return nil, &OpError{"listen", net, nil, errMissingAddress} + return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress} } - fd, err := internetSocket(net, gaddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) + fd, err := internetSocket(net, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP) if err != nil { - return nil, err + return nil, &OpError{Op: "listen", Net: net, Addr: gaddr, Err: err} } c := newUDPConn(fd) if ip4 := gaddr.IP.To4(); ip4 != nil { if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil { c.Close() - return nil, &OpError{"listen", net, &IPAddr{IP: ip4}, err} + return nil, &OpError{Op: "listen", Net: net, Addr: &IPAddr{IP: ip4}, Err: err} } } else { if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil { c.Close() - return nil, &OpError{"listen", net, &IPAddr{IP: gaddr.IP}, err} + return nil, &OpError{Op: "listen", Net: net, Addr: &IPAddr{IP: gaddr.IP}, Err: err} } } return c, nil diff --git a/libgo/go/net/unicast_posix_test.go b/libgo/go/net/unicast_posix_test.go index b0588f4..5deb8f4 100644 --- a/libgo/go/net/unicast_posix_test.go +++ b/libgo/go/net/unicast_posix_test.go @@ -349,12 +349,16 @@ func checkDualStackSecondListener(t *testing.T, net, laddr string, xerr, err err if xerr == nil && err != nil || xerr != nil && err == nil { t.Fatalf("Second Listen(%q, %q) returns %v, expected %v", net, laddr, err, xerr) } - l.(*TCPListener).Close() + if err == nil { + l.(*TCPListener).Close() + } case "udp", "udp4", "udp6": if xerr == nil && err != nil || xerr != nil && err == nil { t.Fatalf("Second ListenPacket(%q, %q) returns %v, expected %v", net, laddr, err, xerr) } - l.(*UDPConn).Close() + if err == nil { + l.(*UDPConn).Close() + } default: t.Fatalf("Unexpected network: %q", net) } @@ -436,8 +440,8 @@ func TestWildWildcardListener(t *testing.T) { } defer func() { - if recover() != nil { - t.Fatalf("panicked") + if p := recover(); p != nil { + t.Fatalf("Listen, ListenPacket or protocol-specific Listen panicked: %v", p) } }() diff --git a/libgo/go/net/unix_test.go b/libgo/go/net/unix_test.go index 5e63e9d9..91df3ff 100644 --- a/libgo/go/net/unix_test.go +++ b/libgo/go/net/unix_test.go @@ -107,7 +107,7 @@ func TestReadUnixgramWithZeroBytesBuffer(t *testing.T) { } } -func TestUnixAutobind(t *testing.T) { +func TestUnixgramAutobind(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("skipping: autobind is linux only") } @@ -139,8 +139,21 @@ func TestUnixAutobind(t *testing.T) { } } +func TestUnixAutobindClose(t *testing.T) { + if runtime.GOOS != "linux" { + t.Skip("skipping: autobind is linux only") + } + laddr := &UnixAddr{Name: "", Net: "unix"} + ln, err := ListenUnix("unix", laddr) + if err != nil { + t.Fatalf("ListenUnix failed: %v", err) + } + ln.Close() +} + func TestUnixConnLocalAndRemoteNames(t *testing.T) { for _, laddr := range []string{"", testUnixAddr()} { + laddr := laddr taddr := testUnixAddr() ta, err := ResolveUnixAddr("unix", taddr) if err != nil { @@ -196,6 +209,7 @@ func TestUnixConnLocalAndRemoteNames(t *testing.T) { func TestUnixgramConnLocalAndRemoteNames(t *testing.T) { for _, laddr := range []string{"", testUnixAddr()} { + laddr := laddr taddr := testUnixAddr() ta, err := ResolveUnixAddr("unixgram", taddr) if err != nil { @@ -212,7 +226,6 @@ func TestUnixgramConnLocalAndRemoteNames(t *testing.T) { var la *UnixAddr if laddr != "" { - var err error if la, err = ResolveUnixAddr("unixgram", laddr); err != nil { t.Fatalf("ResolveUnixAddr failed: %v", err) } diff --git a/libgo/go/net/unixsock.go b/libgo/go/net/unixsock.go index 21a19ec..8595584 100644 --- a/libgo/go/net/unixsock.go +++ b/libgo/go/net/unixsock.go @@ -24,8 +24,8 @@ func (a *UnixAddr) String() string { } func (a *UnixAddr) toAddr() Addr { - if a == nil { // nil *UnixAddr - return nil // nil interface + if a == nil { + return nil } return a } diff --git a/libgo/go/net/unixsock_plan9.go b/libgo/go/net/unixsock_plan9.go index 8a1281f..c60c1d8 100644 --- a/libgo/go/net/unixsock_plan9.go +++ b/libgo/go/net/unixsock_plan9.go @@ -97,7 +97,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { } // AcceptUnix accepts the next incoming call and returns the new -// connection and the remote address. +// connection. func (l *UnixListener) AcceptUnix() (*UnixConn, error) { return nil, syscall.EPLAN9 } diff --git a/libgo/go/net/unixsock_posix.go b/libgo/go/net/unixsock_posix.go index 5db30df..b82f3ce 100644 --- a/libgo/go/net/unixsock_posix.go +++ b/libgo/go/net/unixsock_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -13,14 +13,7 @@ import ( "time" ) -func (a *UnixAddr) isUnnamed() bool { - if a == nil || a.Name == "" { - return true - } - return false -} - -func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (*netFD, error) { +func unixSocket(net string, laddr, raddr sockaddr, mode string, deadline time.Time) (*netFD, error) { var sotype int switch net { case "unix": @@ -33,19 +26,18 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T return nil, UnknownNetworkError(net) } - var la, ra syscall.Sockaddr switch mode { case "dial": - if !laddr.isUnnamed() { - la = &syscall.SockaddrUnix{Name: laddr.Name} + if laddr != nil && laddr.isWildcard() { + laddr = nil } - if raddr != nil { - ra = &syscall.SockaddrUnix{Name: raddr.Name} - } else if sotype != syscall.SOCK_DGRAM || laddr.isUnnamed() { - return nil, &OpError{Op: mode, Net: net, Err: errMissingAddress} + if raddr != nil && raddr.isWildcard() { + raddr = nil + } + if raddr == nil && (sotype != syscall.SOCK_DGRAM || laddr == nil) { + return nil, errMissingAddress } case "listen": - la = &syscall.SockaddrUnix{Name: laddr.Name} default: return nil, errors.New("unknown mode: " + mode) } @@ -57,19 +49,11 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.T f = sockaddrToUnixpacket } - fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f) + fd, err := socket(net, syscall.AF_UNIX, sotype, 0, false, laddr, raddr, deadline, f) if err != nil { - goto error + return nil, err } return fd, nil - -error: - addr := raddr - switch mode { - case "listen": - addr = laddr - } - return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err} } func sockaddrToUnix(sa syscall.Sockaddr) Addr { @@ -106,6 +90,21 @@ func sotypeToNet(sotype int) string { } } +func (a *UnixAddr) family() int { + return syscall.AF_UNIX +} + +func (a *UnixAddr) isWildcard() bool { + return a == nil || a.Name == "" +} + +func (a *UnixAddr) sockaddr(family int) (syscall.Sockaddr, error) { + if a == nil { + return nil, nil + } + return &syscall.SockaddrUnix{Name: a.Name}, nil +} + // UnixConn is an implementation of the Conn interface for connections // to Unix domain sockets. type UnixConn struct { @@ -172,6 +171,9 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) { if !c.ok() { return 0, syscall.EINVAL } + if addr == nil { + return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress} + } if addr.Net != sotypeToNet(c.fd.sotype) { return 0, syscall.EAFNOSUPPORT } @@ -230,18 +232,18 @@ func (c *UnixConn) CloseWrite() error { // which must be "unix", "unixgram" or "unixpacket". If laddr is not // nil, it is used as the local address for the connection. func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) { - return dialUnix(net, laddr, raddr, noDeadline) -} - -func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) { switch net { case "unix", "unixgram", "unixpacket": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: UnknownNetworkError(net)} } + return dialUnix(net, laddr, raddr, noDeadline) +} + +func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) { fd, err := unixSocket(net, laddr, raddr, "dial", deadline) if err != nil { - return nil, err + return nil, &OpError{Op: "dial", Net: net, Addr: raddr, Err: err} } return newUnixConn(fd), nil } @@ -260,25 +262,20 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { switch net { case "unix", "unixpacket": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)} } if laddr == nil { - return nil, &OpError{"listen", net, nil, errMissingAddress} + return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress} } fd, err := unixSocket(net, laddr, nil, "listen", noDeadline) if err != nil { - return nil, err - } - err = syscall.Listen(fd.sysfd, listenerBacklog) - if err != nil { - fd.Close() return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err} } - return &UnixListener{fd, laddr.Name}, nil + return &UnixListener{fd, fd.laddr.String()}, nil } // AcceptUnix accepts the next incoming call and returns the new -// connection and the remote address. +// connection. func (l *UnixListener) AcceptUnix() (*UnixConn, error) { if l == nil || l.fd == nil { return nil, syscall.EINVAL @@ -333,7 +330,7 @@ func (l *UnixListener) SetDeadline(t time.Time) (err error) { if l == nil || l.fd == nil { return syscall.EINVAL } - return setDeadline(l.fd, t) + return l.fd.setDeadline(t) } // File returns a copy of the underlying os.File, set to blocking @@ -353,14 +350,14 @@ func ListenUnixgram(net string, laddr *UnixAddr) (*UnixConn, error) { switch net { case "unixgram": default: - return nil, UnknownNetworkError(net) + return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: UnknownNetworkError(net)} } if laddr == nil { - return nil, &OpError{"listen", net, nil, errMissingAddress} + return nil, &OpError{Op: "listen", Net: net, Addr: nil, Err: errMissingAddress} } fd, err := unixSocket(net, laddr, nil, "listen", noDeadline) if err != nil { - return nil, err + return nil, &OpError{Op: "listen", Net: net, Addr: laddr, Err: err} } return newUnixConn(fd), nil } diff --git a/libgo/go/net/url/url.go b/libgo/go/net/url/url.go index 459dc47..597cb51 100644 --- a/libgo/go/net/url/url.go +++ b/libgo/go/net/url/url.go @@ -451,14 +451,17 @@ func (u *URL) String() string { } else { if u.Scheme != "" || u.Host != "" || u.User != nil { buf.WriteString("//") - if u := u.User; u != nil { - buf.WriteString(u.String()) + if ui := u.User; ui != nil { + buf.WriteString(ui.String()) buf.WriteByte('@') } if h := u.Host; h != "" { buf.WriteString(h) } } + if u.Path != "" && u.Path[0] != '/' && u.Host != "" { + buf.WriteByte('/') + } buf.WriteString(escape(u.Path, encodePath)) } if u.RawQuery != "" { diff --git a/libgo/go/net/url/url_test.go b/libgo/go/net/url/url_test.go index 9d81289..7578eb1 100644 --- a/libgo/go/net/url/url_test.go +++ b/libgo/go/net/url/url_test.go @@ -260,6 +260,14 @@ var urltests = []URLTest{ }, "mailto:webmaster@golang.org", }, + // Relative path + { + "a/b/c", + &URL{ + Path: "a/b/c", + }, + "a/b/c", + }, } // more useful string for debugging than fmt's struct printer @@ -372,6 +380,22 @@ func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, t func TestURLString(t *testing.T) { DoTestString(t, Parse, "Parse", urltests) + + // no leading slash on path should prepend + // slash on String() call + noslash := URLTest{ + "http://www.google.com/search", + &URL{ + Scheme: "http", + Host: "www.google.com", + Path: "search", + }, + "", + } + s := noslash.out.String() + if s != noslash.in { + t.Errorf("Expected %s; go %s", noslash.in, s) + } } type EscapeTest struct { diff --git a/libgo/go/os/dir_unix.go b/libgo/go/os/dir_unix.go index f41f939..9fa7ad6 100644 --- a/libgo/go/os/dir_unix.go +++ b/libgo/go/os/dir_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package os diff --git a/libgo/go/os/doc.go b/libgo/go/os/doc.go index 2cc1753..a954e31 100644 --- a/libgo/go/os/doc.go +++ b/libgo/go/os/doc.go @@ -58,7 +58,7 @@ func (p *ProcessState) SystemTime() time.Duration { return p.systemTime() } -// Exited returns whether the program has exited. +// Exited reports whether the program has exited. func (p *ProcessState) Exited() bool { return p.exited() } @@ -106,6 +106,9 @@ func Hostname() (name string, err error) { // directory, Readdir returns the FileInfo read until that point // and a non-nil error. func (f *File) Readdir(n int) (fi []FileInfo, err error) { + if f == nil { + return nil, ErrInvalid + } return f.readdir(n) } @@ -122,5 +125,8 @@ func (f *File) Readdir(n int) (fi []FileInfo, err error) { // directory, Readdirnames returns the names read until that point and // a non-nil error. func (f *File) Readdirnames(n int) (names []string, err error) { + if f == nil { + return nil, ErrInvalid + } return f.readdirnames(n) } diff --git a/libgo/go/os/env_unix_test.go b/libgo/go/os/env_unix_test.go index 7eb4dc0..e16d71a 100644 --- a/libgo/go/os/env_unix_test.go +++ b/libgo/go/os/env_unix_test.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package os_test diff --git a/libgo/go/os/error.go b/libgo/go/os/error.go index a7977ff..8810e69 100644 --- a/libgo/go/os/error.go +++ b/libgo/go/os/error.go @@ -43,20 +43,23 @@ func NewSyscallError(syscall string, err error) error { return &SyscallError{syscall, err} } -// IsExist returns whether the error is known to report that a file or directory -// already exists. It is satisfied by ErrExist as well as some syscall errors. +// IsExist returns a boolean indicating whether the error is known to report +// that a file or directory already exists. It is satisfied by ErrExist as +// well as some syscall errors. func IsExist(err error) bool { return isExist(err) } -// IsNotExist returns whether the error is known to report that a file or directory -// does not exist. It is satisfied by ErrNotExist as well as some syscall errors. +// IsNotExist returns a boolean indicating whether the error is known to +// report that a file or directory does not exist. It is satisfied by +// ErrNotExist as well as some syscall errors. func IsNotExist(err error) bool { return isNotExist(err) } -// IsPermission returns whether the error is known to report that permission is denied. -// It is satisfied by ErrPermission as well as some syscall errors. +// IsPermission returns a boolean indicating whether the error is known to +// report that permission is denied. It is satisfied by ErrPermission as well +// as some syscall errors. func IsPermission(err error) bool { return isPermission(err) } diff --git a/libgo/go/os/error_posix.go b/libgo/go/os/error_unix.go index 81b626a..6250349 100644 --- a/libgo/go/os/error_posix.go +++ b/libgo/go/os/error_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package os diff --git a/libgo/go/os/exec/exec.go b/libgo/go/os/exec/exec.go index a3bbcf3..491cc24 100644 --- a/libgo/go/os/exec/exec.go +++ b/libgo/go/os/exec/exec.go @@ -13,6 +13,7 @@ import ( "io" "os" "strconv" + "sync" "syscall" ) @@ -357,6 +358,10 @@ func (c *Cmd) CombinedOutput() ([]byte, error) { // StdinPipe returns a pipe that will be connected to the command's // standard input when the command starts. +// The pipe will be closed automatically after Wait sees the command exit. +// A caller need only call Close to force the pipe to close sooner. +// For example, if the command being run will not exit until standard input +// is closed, the caller must close the pipe. func (c *Cmd) StdinPipe() (io.WriteCloser, error) { if c.Stdin != nil { return nil, errors.New("exec: Stdin already set") @@ -370,13 +375,33 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) { } c.Stdin = pr c.closeAfterStart = append(c.closeAfterStart, pr) - c.closeAfterWait = append(c.closeAfterWait, pw) - return pw, nil + wc := &closeOnce{File: pw} + c.closeAfterWait = append(c.closeAfterWait, wc) + return wc, nil +} + +type closeOnce struct { + *os.File + + close sync.Once + closeErr error +} + +func (c *closeOnce) Close() error { + c.close.Do(func() { + c.closeErr = c.File.Close() + }) + return c.closeErr } // StdoutPipe returns a pipe that will be connected to the command's // standard output when the command starts. -// The pipe will be closed automatically after Wait sees the command exit. +// +// Wait will close the pipe after seeing the command exit, so most callers +// need not close the pipe themselves; however, an implication is that +// it is incorrect to call Wait before all reads from the pipe have completed. +// For the same reason, it is incorrect to call Run when using StdoutPipe. +// See the example for idiomatic usage. func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { if c.Stdout != nil { return nil, errors.New("exec: Stdout already set") @@ -396,7 +421,12 @@ func (c *Cmd) StdoutPipe() (io.ReadCloser, error) { // StderrPipe returns a pipe that will be connected to the command's // standard error when the command starts. -// The pipe will be closed automatically after Wait sees the command exit. +// +// Wait will close the pipe after seeing the command exit, so most callers +// need not close the pipe themselves; however, an implication is that +// it is incorrect to call Wait before all reads from the pipe have completed. +// For the same reason, it is incorrect to use Run when using StderrPipe. +// See the StdoutPipe example for idiomatic usage. func (c *Cmd) StderrPipe() (io.ReadCloser, error) { if c.Stderr != nil { return nil, errors.New("exec: Stderr already set") diff --git a/libgo/go/os/exec/exec_test.go b/libgo/go/os/exec/exec_test.go index fa7e88c..b6addcd 100644 --- a/libgo/go/os/exec/exec_test.go +++ b/libgo/go/os/exec/exec_test.go @@ -156,6 +156,34 @@ func TestPipes(t *testing.T) { check("Wait", err) } +const stdinCloseTestString = "Some test string." + +// Issue 6270. +func TestStdinClose(t *testing.T) { + check := func(what string, err error) { + if err != nil { + t.Fatalf("%s: %v", what, err) + } + } + cmd := helperCommand("stdinClose") + stdin, err := cmd.StdinPipe() + check("StdinPipe", err) + // Check that we can access methods of the underlying os.File.` + if _, ok := stdin.(interface { + Fd() uintptr + }); !ok { + t.Error("can't access methods of underlying *os.File") + } + check("Start", cmd.Start()) + go func() { + _, err := io.Copy(stdin, strings.NewReader(stdinCloseTestString)) + check("Copy", err) + // Before the fix, this next line would race with cmd.Wait. + check("Close", stdin.Close()) + }() + check("Wait", cmd.Wait()) +} + // Issue 5071 func TestPipeLookPathLeak(t *testing.T) { fd0 := numOpenFDS(t) @@ -199,8 +227,29 @@ func basefds() uintptr { return n } +func closeUnexpectedFds(t *testing.T, m string) { + for fd := basefds(); fd <= 101; fd++ { + err := os.NewFile(fd, "").Close() + if err == nil { + t.Logf("%s: Something already leaked - closed fd %d", m, fd) + } + } +} + func TestExtraFilesFDShuffle(t *testing.T) { - t.Skip("TODO: TestExtraFilesFDShuffle is too non-portable; skipping") + t.Skip("flaky test; see http://golang.org/issue/5780") + switch runtime.GOOS { + case "darwin": + // TODO(cnicolaou): http://golang.org/issue/2603 + // leads to leaked file descriptors in this test when it's + // run from a builder. + closeUnexpectedFds(t, "TestExtraFilesFDShuffle") + case "netbsd": + // http://golang.org/issue/3955 + closeUnexpectedFds(t, "TestExtraFilesFDShuffle") + case "windows": + t.Skip("no operating system support; skipping") + } // syscall.StartProcess maps all the FDs passed to it in // ProcAttr.Files (the concatenation of stdin,stdout,stderr and @@ -300,12 +349,7 @@ func TestExtraFiles(t *testing.T) { // our environment. if !testedAlreadyLeaked { testedAlreadyLeaked = true - for fd := basefds(); fd <= 101; fd++ { - err := os.NewFile(fd, "").Close() - if err == nil { - t.Logf("Something already leaked - closed fd %d", fd) - } - } + closeUnexpectedFds(t, "TestExtraFiles") } // Force network usage, to verify the epoll (or whatever) fd @@ -434,7 +478,7 @@ func TestHelperProcess(*testing.T) { // Determine which command to use to display open files. ofcmd := "lsof" switch runtime.GOOS { - case "freebsd", "netbsd", "openbsd": + case "dragonfly", "freebsd", "netbsd", "openbsd": ofcmd = "fstat" } @@ -495,6 +539,17 @@ func TestHelperProcess(*testing.T) { os.Exit(1) } } + case "stdinClose": + b, err := ioutil.ReadAll(os.Stdin) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + if s := string(b); s != stdinCloseTestString { + fmt.Fprintf(os.Stderr, "Error: Read %q, want %q", s, stdinCloseTestString) + os.Exit(1) + } + os.Exit(0) case "read3": // read fd 3 fd3 := os.NewFile(3, "fd3") bs, err := ioutil.ReadAll(fd3) @@ -503,6 +558,9 @@ func TestHelperProcess(*testing.T) { os.Exit(1) } switch runtime.GOOS { + case "dragonfly": + // TODO(jsing): Determine why DragonFly is leaking + // file descriptors... case "darwin": // TODO(bradfitz): broken? Sometimes. // http://golang.org/issue/2603 @@ -544,13 +602,11 @@ func TestHelperProcess(*testing.T) { n, _ := strconv.Atoi(args[0]) os.Exit(n) case "describefiles": - for fd := uintptr(3); fd < 25; fd++ { - f := os.NewFile(fd, fmt.Sprintf("fd-%d", fd)) - ln, err := net.FileListener(f) - if err == nil { - fmt.Printf("fd%d: listener %s\n", fd, ln.Addr()) - ln.Close() - } + f := os.NewFile(3, fmt.Sprintf("fd3")) + ln, err := net.FileListener(f) + if err == nil { + fmt.Printf("fd3: listener %s\n", ln.Addr()) + ln.Close() } os.Exit(0) case "extraFilesAndPipes": diff --git a/libgo/go/os/exec/lp_plan9.go b/libgo/go/os/exec/lp_plan9.go index 6846a35..5aa8a54 100644 --- a/libgo/go/os/exec/lp_plan9.go +++ b/libgo/go/os/exec/lp_plan9.go @@ -28,6 +28,7 @@ func findExecutable(file string) error { // in the directories named by the path environment variable. // If file begins with "/", "#", "./", or "../", it is tried // directly and the path is not consulted. +// The result may be an absolute path or a path relative to the current directory. func LookPath(file string) (string, error) { // skip the path lookup for these prefixes skip := []string{"/", "#", "./", "../"} diff --git a/libgo/go/os/exec/lp_unix.go b/libgo/go/os/exec/lp_unix.go index 1d1ec07..7ff2d20 100644 --- a/libgo/go/os/exec/lp_unix.go +++ b/libgo/go/os/exec/lp_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package exec @@ -29,6 +29,7 @@ func findExecutable(file string) error { // LookPath searches for an executable binary named file // in the directories named by the PATH environment variable. // If file contains a slash, it is tried directly and the PATH is not consulted. +// The result may be an absolute path or a path relative to the current directory. func LookPath(file string) (string, error) { // NOTE(rsc): I wish we could use the Plan 9 behavior here // (only bypass the path if file begins with / or ./ or ../) diff --git a/libgo/go/os/exec/lp_unix_test.go b/libgo/go/os/exec/lp_unix_test.go index 625d784..f1ab6de 100644 --- a/libgo/go/os/exec/lp_unix_test.go +++ b/libgo/go/os/exec/lp_unix_test.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package exec diff --git a/libgo/go/os/exec/lp_windows.go b/libgo/go/os/exec/lp_windows.go index 7c7289b..c3efd67 100644 --- a/libgo/go/os/exec/lp_windows.go +++ b/libgo/go/os/exec/lp_windows.go @@ -24,14 +24,21 @@ func chkStat(file string) error { return nil } +func hasExt(file string) bool { + i := strings.LastIndex(file, ".") + if i < 0 { + return false + } + return strings.LastIndexAny(file, `:\/`) < i +} + func findExecutable(file string, exts []string) (string, error) { if len(exts) == 0 { return file, chkStat(file) } - f := strings.ToLower(file) - for _, e := range exts { - if strings.HasSuffix(f, e) { - return file, chkStat(file) + if hasExt(file) { + if chkStat(file) == nil { + return file, nil } } for _, e := range exts { @@ -47,6 +54,7 @@ func findExecutable(file string, exts []string) (string, error) { // If file contains a slash, it is tried directly and the PATH is not consulted. // LookPath also uses PATHEXT environment variable to match // a suitable candidate. +// The result may be an absolute path or a path relative to the current directory. func LookPath(file string) (f string, err error) { x := os.Getenv(`PATHEXT`) if x == `` { diff --git a/libgo/go/os/exec_posix.go b/libgo/go/os/exec_posix.go index f7b10f3..fb123aef 100644 --- a/libgo/go/os/exec_posix.go +++ b/libgo/go/os/exec_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package os diff --git a/libgo/go/os/exec_unix.go b/libgo/go/os/exec_unix.go index fa3ba8a..5572e62 100644 --- a/libgo/go/os/exec_unix.go +++ b/libgo/go/os/exec_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package os diff --git a/libgo/go/os/exec_windows.go b/libgo/go/os/exec_windows.go index 4aa2ade..c4f3d4f 100644 --- a/libgo/go/os/exec_windows.go +++ b/libgo/go/os/exec_windows.go @@ -42,13 +42,22 @@ func (p *Process) wait() (ps *ProcessState, err error) { return &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil } +func terminateProcess(pid, exitcode int) error { + h, e := syscall.OpenProcess(syscall.PROCESS_TERMINATE, false, uint32(pid)) + if e != nil { + return NewSyscallError("OpenProcess", e) + } + defer syscall.CloseHandle(h) + e = syscall.TerminateProcess(h, uint32(exitcode)) + return NewSyscallError("TerminateProcess", e) +} + func (p *Process) signal(sig Signal) error { if p.done() { return errors.New("os: process already finished") } if sig == Kill { - e := syscall.TerminateProcess(syscall.Handle(p.handle), 1) - return NewSyscallError("TerminateProcess", e) + return terminateProcess(p.Pid, 1) } // TODO(rsc): Handle Interrupt too? return syscall.Errno(syscall.EWINDOWS) diff --git a/libgo/go/os/export_test.go b/libgo/go/os/export_test.go index 9c6ef42..9fa7936 100644 --- a/libgo/go/os/export_test.go +++ b/libgo/go/os/export_test.go @@ -7,3 +7,4 @@ package os // Export for testing. var Atime = atime +var LstatP = &lstat diff --git a/libgo/go/os/file.go b/libgo/go/os/file.go index 32cac6d..2dd1fcf 100644 --- a/libgo/go/os/file.go +++ b/libgo/go/os/file.go @@ -174,6 +174,9 @@ func (f *File) WriteAt(b []byte, off int64) (n int, err error) { // relative to the current offset, and 2 means relative to the end. // It returns the new offset and an error, if any. func (f *File) Seek(offset int64, whence int) (ret int64, err error) { + if f == nil { + return 0, ErrInvalid + } r, e := f.seek(offset, whence) if e == nil && f.dirinfo != nil && r != 0 { e = syscall.EISDIR @@ -216,6 +219,9 @@ func Chdir(dir string) error { // which must be a directory. // If there is an error, it will be of type *PathError. func (f *File) Chdir() error { + if f == nil { + return ErrInvalid + } if e := syscall.Fchdir(f.fd); e != nil { return &PathError{"chdir", f.name, e} } @@ -238,3 +244,6 @@ func Open(name string) (file *File, err error) { func Create(name string) (file *File, err error) { return OpenFile(name, O_RDWR|O_CREATE|O_TRUNC, 0666) } + +// lstat is overridden in tests. +var lstat = Lstat diff --git a/libgo/go/os/file_plan9.go b/libgo/go/os/file_plan9.go index d6d39a8..708163e 100644 --- a/libgo/go/os/file_plan9.go +++ b/libgo/go/os/file_plan9.go @@ -133,6 +133,9 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { // Close closes the File, rendering it unusable for I/O. // It returns an error, if any. func (f *File) Close() error { + if f == nil { + return ErrInvalid + } return f.file.close() } @@ -156,6 +159,9 @@ func (file *file) close() error { // Stat returns the FileInfo structure describing file. // If there is an error, it will be of type *PathError. func (f *File) Stat() (fi FileInfo, err error) { + if f == nil { + return nil, ErrInvalid + } d, err := dirstat(f) if err != nil { return nil, err @@ -167,8 +173,11 @@ func (f *File) Stat() (fi FileInfo, err error) { // It does not change the I/O offset. // If there is an error, it will be of type *PathError. func (f *File) Truncate(size int64) error { - var d syscall.Dir + if f == nil { + return ErrInvalid + } + var d syscall.Dir d.Null() d.Length = size @@ -188,6 +197,9 @@ const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | Mod // Chmod changes the mode of the file to mode. // If there is an error, it will be of type *PathError. func (f *File) Chmod(mode FileMode) error { + if f == nil { + return ErrInvalid + } var d syscall.Dir odir, e := dirstat(f) @@ -419,6 +431,9 @@ func Lchown(name string, uid, gid int) error { // Chown changes the numeric uid and gid of the named file. // If there is an error, it will be of type *PathError. func (f *File) Chown(uid, gid int) error { + if f == nil { + return ErrInvalid + } return &PathError{"chown", f.name, syscall.EPLAN9} } diff --git a/libgo/go/os/file_posix.go b/libgo/go/os/file_posix.go index 3df43fe..a8bef35 100644 --- a/libgo/go/os/file_posix.go +++ b/libgo/go/os/file_posix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package os @@ -86,6 +86,9 @@ func Chmod(name string, mode FileMode) error { // Chmod changes the mode of the file to mode. // If there is an error, it will be of type *PathError. func (f *File) Chmod(mode FileMode) error { + if f == nil { + return ErrInvalid + } if e := syscall.Fchmod(f.fd, syscallMode(mode)); e != nil { return &PathError{"chmod", f.name, e} } @@ -115,6 +118,9 @@ func Lchown(name string, uid, gid int) error { // Chown changes the numeric uid and gid of the named file. // If there is an error, it will be of type *PathError. func (f *File) Chown(uid, gid int) error { + if f == nil { + return ErrInvalid + } if e := syscall.Fchown(f.fd, uid, gid); e != nil { return &PathError{"chown", f.name, e} } @@ -125,6 +131,9 @@ func (f *File) Chown(uid, gid int) error { // It does not change the I/O offset. // If there is an error, it will be of type *PathError. func (f *File) Truncate(size int64) error { + if f == nil { + return ErrInvalid + } if e := syscall.Ftruncate(f.fd, size); e != nil { return &PathError{"truncate", f.name, e} } diff --git a/libgo/go/os/file_unix.go b/libgo/go/os/file_unix.go index 993a438..79eeaec 100644 --- a/libgo/go/os/file_unix.go +++ b/libgo/go/os/file_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package os @@ -95,6 +95,9 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) { // Close closes the File, rendering it unusable for I/O. // It returns an error, if any. func (f *File) Close() error { + if f == nil { + return ErrInvalid + } return f.file.close() } @@ -128,6 +131,9 @@ func (file *file) close() error { // Stat returns the FileInfo structure describing file. // If there is an error, it will be of type *PathError. func (f *File) Stat() (fi FileInfo, err error) { + if f == nil { + return nil, ErrInvalid + } var stat syscall.Stat_t err = syscall.Fstat(f.fd, &stat) if err != nil { @@ -169,11 +175,14 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) { names, err := f.Readdirnames(n) fi = make([]FileInfo, len(names)) for i, filename := range names { - fip, err := Lstat(dirname + filename) - if err == nil { + fip, lerr := lstat(dirname + filename) + if lerr == nil { fi[i] = fip } else { fi[i] = &fileStat{name: filename} + if err == nil { + err = lerr + } } } return fi, err diff --git a/libgo/go/os/getwd.go b/libgo/go/os/getwd.go index 0235c5d..8c5ff7f 100644 --- a/libgo/go/os/getwd.go +++ b/libgo/go/os/getwd.go @@ -14,6 +14,10 @@ var getwdCache struct { dir string } +// useSyscallwd determines whether to use the return value of +// syscall.Getwd based on its error. +var useSyscallwd = func(error) bool { return true } + // Getwd returns a rooted path name corresponding to the // current directory. If the current directory can be // reached via multiple paths (due to symbolic links), @@ -22,7 +26,9 @@ func Getwd() (pwd string, err error) { // If the operating system provides a Getwd call, use it. if syscall.ImplementsGetwd { s, e := syscall.Getwd() - return s, NewSyscallError("getwd", e) + if useSyscallwd(e) { + return s, NewSyscallError("getwd", e) + } } // Otherwise, we're trying to find our way back to ".". diff --git a/libgo/go/os/getwd_darwin.go b/libgo/go/os/getwd_darwin.go new file mode 100644 index 0000000..e51ffcd --- /dev/null +++ b/libgo/go/os/getwd_darwin.go @@ -0,0 +1,15 @@ +// 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" + +func init() { + useSyscallwd = useSyscallwdDarwin +} + +func useSyscallwdDarwin(err error) bool { + return err != syscall.ENOTSUP +} diff --git a/libgo/go/os/os_test.go b/libgo/go/os/os_test.go index 40ca44d..972df36 100644 --- a/libgo/go/os/os_test.go +++ b/libgo/go/os/os_test.go @@ -11,11 +11,13 @@ import ( "io" "io/ioutil" . "os" + osexec "os/exec" "path/filepath" "runtime" "strings" "syscall" "testing" + "text/template" "time" ) @@ -295,6 +297,7 @@ func TestReaddirnamesOneAtATime(t *testing.T) { if err2 != nil { t.Fatalf("open %q failed: %v", dir, err2) } + defer file1.Close() small := smallReaddirnames(file1, len(all)+100, t) // +100 in case we screw up if len(small) < len(all) { t.Fatalf("len(small) is %d, less than %d", len(small), len(all)) @@ -522,6 +525,7 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) { if err != nil { t.Fatalf("Pipe: %v", err) } + defer r.Close() attr := &ProcAttr{Dir: dir, Files: []*File{nil, w, Stderr}} p, err := StartProcess(cmd, args, attr) if err != nil { @@ -819,9 +823,16 @@ func TestOpenError(t *testing.T) { if !strings.HasSuffix(syscallErrStr, expectedErrStr) { t.Errorf("Open(%q, %d) = _, %q; want suffix %q", tt.path, tt.mode, syscallErrStr, expectedErrStr) } - } else { - t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error()) + continue + } + if runtime.GOOS == "dragonfly" { + // DragonFly incorrectly returns EACCES rather + // EISDIR when a directory is opened for write. + if tt.error == syscall.EISDIR && perr.Err == syscall.EACCES { + continue + } } + t.Errorf("Open(%q, %d) = _, %q; want %q", tt.path, tt.mode, perr.Err.Error(), tt.error.Error()) } } } @@ -840,6 +851,7 @@ func run(t *testing.T, cmd []string) string { if err != nil { t.Fatal(err) } + defer r.Close() p, err := StartProcess("/bin/hostname", []string{"hostname"}, &ProcAttr{Files: []*File{nil, w, Stderr}}) if err != nil { t.Fatal(err) @@ -1112,3 +1124,88 @@ func TestStatDirModeExec(t *testing.T) { t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode) } } + +func TestReadAtEOF(t *testing.T) { + f := newFile("TestReadAtEOF", t) + defer Remove(f.Name()) + defer f.Close() + + _, err := f.ReadAt(make([]byte, 10), 0) + switch err { + case io.EOF: + // all good + case nil: + t.Fatalf("ReadAt succeeded") + default: + t.Fatalf("ReadAt failed: %s", err) + } +} + +func testKillProcess(t *testing.T, processKiller func(p *Process)) { + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("Failed to create temp directory: %v", err) + } + defer RemoveAll(dir) + + src := filepath.Join(dir, "main.go") + f, err := Create(src) + if err != nil { + t.Fatalf("Failed to create %v: %v", src, err) + } + st := template.Must(template.New("source").Parse(` +package main +import "time" +func main() { + time.Sleep(time.Second) +} +`)) + err = st.Execute(f, nil) + if err != nil { + f.Close() + t.Fatalf("Failed to execute template: %v", err) + } + f.Close() + + exe := filepath.Join(dir, "main.exe") + output, err := osexec.Command("go", "build", "-o", exe, src).CombinedOutput() + if err != nil { + t.Fatalf("Failed to build exe %v: %v %v", exe, err, string(output)) + } + + cmd := osexec.Command(exe) + err = cmd.Start() + if err != nil { + t.Fatalf("Failed to start test process: %v", err) + } + go func() { + time.Sleep(100 * time.Millisecond) + processKiller(cmd.Process) + }() + err = cmd.Wait() + if err == nil { + t.Errorf("Test process succeeded, but expected to fail") + } +} + +func TestKillStartProcess(t *testing.T) { + testKillProcess(t, func(p *Process) { + err := p.Kill() + if err != nil { + t.Fatalf("Failed to kill test process: %v", err) + } + }) +} + +func TestKillFindProcess(t *testing.T) { + testKillProcess(t, func(p *Process) { + p2, err := FindProcess(p.Pid) + if err != nil { + t.Fatalf("Failed to find test process: %v", err) + } + err = p2.Kill() + if err != nil { + t.Fatalf("Failed to kill test process: %v", err) + } + }) +} diff --git a/libgo/go/os/os_unix_test.go b/libgo/go/os/os_unix_test.go index f8e330b..80d57aa 100644 --- a/libgo/go/os/os_unix_test.go +++ b/libgo/go/os/os_unix_test.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package os_test @@ -28,7 +28,7 @@ func checkUidGid(t *testing.T, path string, uid, gid int) { } func TestChown(t *testing.T) { - // Chown is not supported under windows or Plan 9. + // Chown is not supported under windows os Plan 9. // Plan9 provides a native ChownPlan9 version instead. if runtime.GOOS == "windows" || runtime.GOOS == "plan9" { return @@ -74,3 +74,41 @@ func TestChown(t *testing.T) { checkUidGid(t, f.Name(), int(sys.Uid), gid) } } + +func TestReaddirWithBadLstat(t *testing.T) { + handle, err := Open(sfdir) + failfile := sfdir + "/" + sfname + if err != nil { + t.Fatalf("Couldn't open %s: %s", sfdir, err) + } + + *LstatP = func(file string) (FileInfo, error) { + if file == failfile { + var fi FileInfo + return fi, ErrInvalid + } + return Lstat(file) + } + defer func() { *LstatP = Lstat }() + + dirs, err := handle.Readdir(-1) + if err != ErrInvalid { + t.Fatalf("Expected Readdir to return ErrInvalid, got %v", err) + } + foundfail := false + for _, dir := range dirs { + if dir.Name() == sfname { + foundfail = true + if dir.Sys() != nil { + t.Errorf("Expected Readdir for %s should not contain Sys", failfile) + } + } else { + if dir.Sys() == nil { + t.Errorf("Readdir for every file other than %s should contain Sys, but %s/%s didn't either", failfile, sfdir, dir.Name()) + } + } + } + if !foundfail { + t.Fatalf("Expected %s from Readdir, but didn't find it", failfile) + } +} diff --git a/libgo/go/os/path_test.go b/libgo/go/os/path_test.go index 16c4120..27abf59 100644 --- a/libgo/go/os/path_test.go +++ b/libgo/go/os/path_test.go @@ -91,7 +91,7 @@ func TestRemoveAll(t *testing.T) { if err = RemoveAll(path); err != nil { t.Fatalf("RemoveAll %q (first): %s", path, err) } - if _, err := Lstat(path); err == nil { + if _, err = Lstat(path); err == nil { t.Fatalf("Lstat %q succeeded after RemoveAll (first)", path) } @@ -153,7 +153,7 @@ func TestRemoveAll(t *testing.T) { Chmod(dpath, 0777) for _, s := range []string{fpath, path + "/zzz"} { - if _, err := Lstat(s); err == nil { + if _, err = Lstat(s); err == nil { t.Fatalf("Lstat %q succeeded after partial RemoveAll", s) } } @@ -161,7 +161,7 @@ func TestRemoveAll(t *testing.T) { if err = RemoveAll(path); err != nil { t.Fatalf("RemoveAll %q after partial RemoveAll: %s", path, err) } - if _, err := Lstat(path); err == nil { + if _, err = Lstat(path); err == nil { t.Fatalf("Lstat %q succeeded after RemoveAll (final)", path) } } diff --git a/libgo/go/os/path_unix.go b/libgo/go/os/path_unix.go index 30a167b..3bf63bf 100644 --- a/libgo/go/os/path_unix.go +++ b/libgo/go/os/path_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package os diff --git a/libgo/go/os/pipe_bsd.go b/libgo/go/os/pipe_bsd.go index a2ce9a3..73d35b4 100644 --- a/libgo/go/os/pipe_bsd.go +++ b/libgo/go/os/pipe_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package os diff --git a/libgo/go/os/signal/signal_test.go b/libgo/go/os/signal/signal_test.go index d138333..741f2a0 100644 --- a/libgo/go/os/signal/signal_test.go +++ b/libgo/go/os/signal/signal_test.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package signal @@ -36,8 +36,8 @@ func TestSignal(t *testing.T) { Notify(c, syscall.SIGHUP) defer Stop(c) - t.Logf("sighup...") // Send this process a SIGHUP + t.Logf("sighup...") syscall.Kill(syscall.Getpid(), syscall.SIGHUP) waitSig(t, c, syscall.SIGHUP) @@ -45,18 +45,18 @@ func TestSignal(t *testing.T) { c1 := make(chan os.Signal, 1) Notify(c1) - t.Logf("sigwinch...") // Send this process a SIGWINCH + t.Logf("sigwinch...") syscall.Kill(syscall.Getpid(), syscall.SIGWINCH) waitSig(t, c1, syscall.SIGWINCH) // Send two more SIGHUPs, to make sure that // they get delivered on c1 and that not reading // from c does not block everything. - t.Logf("sigwinch...") + t.Logf("sighup...") syscall.Kill(syscall.Getpid(), syscall.SIGHUP) waitSig(t, c1, syscall.SIGHUP) - t.Logf("sigwinch...") + t.Logf("sighup...") syscall.Kill(syscall.Getpid(), syscall.SIGHUP) waitSig(t, c1, syscall.SIGHUP) diff --git a/libgo/go/os/signal/signal_unix.go b/libgo/go/os/signal/signal_unix.go index 6b4c8ab..318488d 100644 --- a/libgo/go/os/signal/signal_unix.go +++ b/libgo/go/os/signal/signal_unix.go @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows package signal diff --git a/libgo/go/os/stat_dragonfly.go b/libgo/go/os/stat_dragonfly.go new file mode 100644 index 0000000..605c1d9 --- /dev/null +++ b/libgo/go/os/stat_dragonfly.go @@ -0,0 +1,61 @@ +// 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.Mtim), + 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(ts syscall.Timespec) time.Time { + return time.Unix(int64(ts.Sec), int64(ts.Nsec)) +} + +// For testing. +func atime(fi FileInfo) time.Time { + return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim) +} diff --git a/libgo/go/os/sys_bsd.go b/libgo/go/os/sys_bsd.go index 0f263f1..9ad2f85 100644 --- a/libgo/go/os/sys_bsd.go +++ b/libgo/go/os/sys_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd // os code shared between *BSD systems including OS X (Darwin) // and FreeBSD. diff --git a/libgo/go/os/user/lookup_plan9.go b/libgo/go/os/user/lookup_plan9.go new file mode 100644 index 0000000..f7ef348 --- /dev/null +++ b/libgo/go/os/user/lookup_plan9.go @@ -0,0 +1,46 @@ +// 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 user + +import ( + "fmt" + "io/ioutil" + "os" + "syscall" +) + +// Partial os/user support on Plan 9. +// Supports Current(), but not Lookup()/LookupId(). +// The latter two would require parsing /adm/users. +const ( + userFile = "/dev/user" +) + +func current() (*User, error) { + ubytes, err := ioutil.ReadFile(userFile) + if err != nil { + return nil, fmt.Errorf("user: %s", err) + } + + uname := string(ubytes) + + u := &User{ + Uid: uname, + Gid: uname, + Username: uname, + Name: uname, + HomeDir: os.Getenv("home"), + } + + return u, nil +} + +func lookup(username string) (*User, error) { + return nil, syscall.EPLAN9 +} + +func lookupId(uid string) (*User, error) { + return nil, syscall.EPLAN9 +} diff --git a/libgo/go/os/user/lookup_stubs.go b/libgo/go/os/user/lookup_stubs.go index ad06907..86f0e6e 100644 --- a/libgo/go/os/user/lookup_stubs.go +++ b/libgo/go/os/user/lookup_stubs.go @@ -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 !cgo,!windows +// +build !cgo,!windows,!plan9 package user diff --git a/libgo/go/os/user/lookup_unix.go b/libgo/go/os/user/lookup_unix.go index e1e2777..eca97a6 100644 --- a/libgo/go/os/user/lookup_unix.go +++ b/libgo/go/os/user/lookup_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // +build cgo package user diff --git a/libgo/go/os/user/lookup_windows.go b/libgo/go/os/user/lookup_windows.go index a0a8a4e..99c325f 100644 --- a/libgo/go/os/user/lookup_windows.go +++ b/libgo/go/os/user/lookup_windows.go @@ -10,37 +10,63 @@ import ( "unsafe" ) -func lookupFullName(domain, username, domainAndUser string) (string, error) { - // try domain controller first - name, e := syscall.TranslateAccountName(domainAndUser, +func isDomainJoined() (bool, error) { + var domain *uint16 + var status uint32 + err := syscall.NetGetJoinInformation(nil, &domain, &status) + if err != nil { + return false, err + } + syscall.NetApiBufferFree((*byte)(unsafe.Pointer(domain))) + return status == syscall.NetSetupDomainName, nil +} + +func lookupFullNameDomain(domainAndUser string) (string, error) { + return syscall.TranslateAccountName(domainAndUser, syscall.NameSamCompatible, syscall.NameDisplay, 50) +} + +func lookupFullNameServer(servername, username string) (string, error) { + s, e := syscall.UTF16PtrFromString(servername) if e != nil { - // domain lookup failed, perhaps this pc is not part of domain - d, e := syscall.UTF16PtrFromString(domain) - if e != nil { - return "", e - } - u, e := syscall.UTF16PtrFromString(username) - if e != nil { - return "", e - } - var p *byte - e = syscall.NetUserGetInfo(d, u, 10, &p) - if e != nil { - // path executed when a domain user is disconnected from the domain - // pretend username is fullname - return username, nil - } - defer syscall.NetApiBufferFree(p) - i := (*syscall.UserInfo10)(unsafe.Pointer(p)) - if i.FullName == nil { - return "", nil - } - name = syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:]) + return "", e } + u, e := syscall.UTF16PtrFromString(username) + if e != nil { + return "", e + } + var p *byte + e = syscall.NetUserGetInfo(s, u, 10, &p) + if e != nil { + return "", e + } + defer syscall.NetApiBufferFree(p) + i := (*syscall.UserInfo10)(unsafe.Pointer(p)) + if i.FullName == nil { + return "", nil + } + name := syscall.UTF16ToString((*[1024]uint16)(unsafe.Pointer(i.FullName))[:]) return name, nil } +func lookupFullName(domain, username, domainAndUser string) (string, error) { + joined, err := isDomainJoined() + if err == nil && joined { + name, err := lookupFullNameDomain(domainAndUser) + if err == nil { + return name, nil + } + } + name, err := lookupFullNameServer(domain, username) + if err == nil { + return name, nil + } + // domain worked neigher as a domain nor as a server + // could be domain server unavailable + // pretend username is fullname + return username, nil +} + func newUser(usid *syscall.SID, gid, dir string) (*User, error) { username, domain, t, e := usid.LookupAccount("") if e != nil { @@ -73,6 +99,7 @@ func current() (*User, error) { if e != nil { return nil, e } + defer t.Close() u, e := t.GetTokenUser() if e != nil { return nil, e diff --git a/libgo/go/os/user/user.go b/libgo/go/os/user/user.go index 841f2263..e8680fe 100644 --- a/libgo/go/os/user/user.go +++ b/libgo/go/os/user/user.go @@ -16,6 +16,8 @@ var implemented = true // set to false by lookup_stubs.go's init // On posix systems Uid and Gid contain a decimal number // representing uid and gid. On windows Uid and Gid // contain security identifier (SID) in a string format. +// On Plan 9, Uid, Gid, Username, and Name will be the +// contents of /dev/user. type User struct { Uid string // user id Gid string // primary group id diff --git a/libgo/go/os/user/user_test.go b/libgo/go/os/user/user_test.go index 444a9aa..9d9420e 100644 --- a/libgo/go/os/user/user_test.go +++ b/libgo/go/os/user/user_test.go @@ -13,12 +13,6 @@ func check(t *testing.T) { if !implemented { t.Skip("user: not implemented; skipping tests") } - switch runtime.GOOS { - case "linux", "freebsd", "darwin", "windows": - // test supported - default: - t.Skipf("user: Lookup not implemented on %q; skipping test", runtime.GOOS) - } } func TestCurrent(t *testing.T) { @@ -61,6 +55,10 @@ func compare(t *testing.T, want, got *User) { func TestLookup(t *testing.T) { check(t) + if runtime.GOOS == "plan9" { + t.Skipf("Lookup not implemented on %q", runtime.GOOS) + } + want, err := Current() if err != nil { t.Fatalf("Current: %v", err) @@ -75,6 +73,10 @@ func TestLookup(t *testing.T) { func TestLookupId(t *testing.T) { check(t) + if runtime.GOOS == "plan9" { + t.Skipf("LookupId not implemented on %q", runtime.GOOS) + } + want, err := Current() if err != nil { t.Fatalf("Current: %v", err) diff --git a/libgo/go/path/filepath/match.go b/libgo/go/path/filepath/match.go index db8b026..3d84145 100644 --- a/libgo/go/path/filepath/match.go +++ b/libgo/go/path/filepath/match.go @@ -132,6 +132,12 @@ func matchChunk(chunk, s string) (rest string, ok bool, err error) { r, n := utf8.DecodeRuneInString(s) s = s[n:] chunk = chunk[1:] + // We can't end right after '[', we're expecting at least + // a closing bracket and possibly a caret. + if len(chunk) == 0 { + err = ErrBadPattern + return + } // possibly negated negated := chunk[0] == '^' if negated { diff --git a/libgo/go/path/filepath/match_test.go b/libgo/go/path/filepath/match_test.go index 1095cc5..6a2fd92 100644 --- a/libgo/go/path/filepath/match_test.go +++ b/libgo/go/path/filepath/match_test.go @@ -65,6 +65,11 @@ var matchTests = []MatchTest{ {"[-x]", "a", false, ErrBadPattern}, {"\\", "a", false, ErrBadPattern}, {"[a-b-c]", "a", false, ErrBadPattern}, + {"[", "a", false, ErrBadPattern}, + {"[^", "a", false, ErrBadPattern}, + {"[^bc", "a", false, ErrBadPattern}, + {"a[", "a", false, nil}, + {"a[", "ab", false, ErrBadPattern}, {"*x", "xxx", true, nil}, } diff --git a/libgo/go/path/filepath/path_test.go b/libgo/go/path/filepath/path_test.go index 607bfed..3a6e83d 100644 --- a/libgo/go/path/filepath/path_test.go +++ b/libgo/go/path/filepath/path_test.go @@ -107,6 +107,9 @@ func TestClean(t *testing.T) { } } + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") return @@ -633,6 +636,10 @@ 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") + } + tmpDir, err := ioutil.TempDir("", "evalsymlink") if err != nil { t.Fatal("creating temp dir:", err) @@ -926,28 +933,33 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) { differently. func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486 - root, err := filepath.EvalSymlinks(runtime.GOROOT()) + root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test") if err != nil { t.Fatal(err) } - lib := filepath.Join(root, "lib") - src := filepath.Join(root, "src") - seenSrc := false + bugs := filepath.Join(root, "bugs") + ken := filepath.Join(root, "ken") + seenBugs := false + seenKen := false filepath.Walk(root, func(pth string, info os.FileInfo, err error) error { if err != nil { t.Fatal(err) } switch pth { - case lib: + case bugs: + seenBugs = true return filepath.SkipDir - case src: - seenSrc = true + case ken: + if !seenBugs { + t.Fatal("filepath.Walk out of order - ken before bugs") + } + seenKen = true } return nil }) - if !seenSrc { - t.Fatalf("%q not seen", src) + if !seenKen { + t.Fatalf("%q not seen", ken) } } diff --git a/libgo/go/path/filepath/path_unix.go b/libgo/go/path/filepath/path_unix.go index cff7b2c..d927b34 100644 --- a/libgo/go/path/filepath/path_unix.go +++ b/libgo/go/path/filepath/path_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package filepath diff --git a/libgo/go/path/match_test.go b/libgo/go/path/match_test.go index 730b6b9..6b0676f 100644 --- a/libgo/go/path/match_test.go +++ b/libgo/go/path/match_test.go @@ -61,6 +61,11 @@ var matchTests = []MatchTest{ {"[-x]", "a", false, ErrBadPattern}, {"\\", "a", false, ErrBadPattern}, {"[a-b-c]", "a", false, ErrBadPattern}, + {"[", "a", false, ErrBadPattern}, + {"[^", "a", false, ErrBadPattern}, + {"[^bc", "a", false, ErrBadPattern}, + {"a[", "a", false, nil}, + {"a[", "ab", false, ErrBadPattern}, {"*x", "xxx", true, nil}, } diff --git a/libgo/go/path/path_test.go b/libgo/go/path/path_test.go index 6297440..512d936 100644 --- a/libgo/go/path/path_test.go +++ b/libgo/go/path/path_test.go @@ -72,7 +72,12 @@ func TestClean(t *testing.T) { t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) } } +} +func TestCleanMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") return diff --git a/libgo/go/reflect/all_test.go b/libgo/go/reflect/all_test.go index 526f09b..6ab02f7 100644 --- a/libgo/go/reflect/all_test.go +++ b/libgo/go/reflect/all_test.go @@ -169,16 +169,20 @@ var typeTests = []pair{ } var valueTests = []pair{ + {new(int), "132"}, {new(int8), "8"}, {new(int16), "16"}, {new(int32), "32"}, {new(int64), "64"}, + {new(uint), "132"}, {new(uint8), "8"}, {new(uint16), "16"}, {new(uint32), "32"}, {new(uint64), "64"}, {new(float32), "256.25"}, {new(float64), "512.125"}, + {new(complex64), "532.125+10i"}, + {new(complex128), "564.25+1i"}, {new(string), "stringy cheese"}, {new(bool), "true"}, {new(*int8), "*int8(0)"}, @@ -944,7 +948,7 @@ func TestMap(t *testing.T) { newm := newmap.Interface().(map[string]int) if len(newm) != len(m) { - t.Errorf("length after copy: newm=%d, m=%d", newm, m) + t.Errorf("length after copy: newm=%d, m=%d", len(newm), len(m)) } for k, v := range newm { @@ -1630,6 +1634,25 @@ func TestMethodValue(t *testing.T) { t.Errorf("Pointer Value MethodByName returned %d; want 325", i) } + // Curried method of pointer to pointer. + pp := &p + v = ValueOf(&pp).Elem().Method(1) + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Pointer Value Method Type is %s; want %s", tt, tfunc) + } + i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int() + if i != 350 { + t.Errorf("Pointer Pointer Value Method returned %d; want 350", i) + } + v = ValueOf(&pp).Elem().MethodByName("Dist") + if tt := v.Type(); tt != tfunc { + t.Errorf("Pointer Pointer Value MethodByName Type is %s; want %s", tt, tfunc) + } + i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int() + if i != 375 { + t.Errorf("Pointer Pointer Value MethodByName returned %d; want 375", i) + } + // Curried method of interface value. // Have to wrap interface value in a struct to get at it. // Passing it to ValueOf directly would @@ -1644,17 +1667,17 @@ func TestMethodValue(t *testing.T) { if tt := v.Type(); tt != tfunc { t.Errorf("Interface Method Type is %s; want %s", tt, tfunc) } - i = ValueOf(v.Interface()).Call([]Value{ValueOf(14)})[0].Int() - if i != 350 { - t.Errorf("Interface Method returned %d; want 350", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(16)})[0].Int() + if i != 400 { + t.Errorf("Interface Method returned %d; want 400", i) } v = pv.MethodByName("Dist") if tt := v.Type(); tt != tfunc { t.Errorf("Interface MethodByName Type is %s; want %s", tt, tfunc) } - i = ValueOf(v.Interface()).Call([]Value{ValueOf(15)})[0].Int() - if i != 375 { - t.Errorf("Interface MethodByName returned %d; want 375", i) + i = ValueOf(v.Interface()).Call([]Value{ValueOf(17)})[0].Int() + if i != 425 { + t.Errorf("Interface MethodByName returned %d; want 425", i) } } @@ -2330,6 +2353,9 @@ func TestAddr(t *testing.T) { /* gccgo does do allocations here. func noAlloc(t *testing.T, n int, f func(int)) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -2413,6 +2439,74 @@ func TestSlice(t *testing.T) { } } +func TestSlice3(t *testing.T) { + xs := []int{1, 2, 3, 4, 5, 6, 7, 8} + v := ValueOf(xs).Slice3(3, 5, 7).Interface().([]int) + if len(v) != 2 { + t.Errorf("len(xs.Slice3(3, 5, 7)) = %d", len(v)) + } + if cap(v) != 4 { + t.Errorf("cap(xs.Slice3(3, 5, 7)) = %d", cap(v)) + } + if !DeepEqual(v[0:4], xs[3:7:7]) { + t.Errorf("xs.Slice3(3, 5, 7)[0:4] = %v", v[0:4]) + } + rv := ValueOf(&xs).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 1) }) + shouldPanic(func() { rv.Slice3(1, 1, 11) }) + shouldPanic(func() { rv.Slice3(2, 2, 1) }) + + xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80} + v = ValueOf(&xa).Elem().Slice3(2, 5, 6).Interface().([]int) + if len(v) != 3 { + t.Errorf("len(xa.Slice(2, 5, 6)) = %d", len(v)) + } + if cap(v) != 4 { + t.Errorf("cap(xa.Slice(2, 5, 6)) = %d", cap(v)) + } + if !DeepEqual(v[0:4], xa[2:6:6]) { + t.Errorf("xs.Slice(2, 5, 6)[0:4] = %v", v[0:4]) + } + rv = ValueOf(&xa).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 1) }) + shouldPanic(func() { rv.Slice3(1, 1, 11) }) + shouldPanic(func() { rv.Slice3(2, 2, 1) }) + + s := "hello world" + rv = ValueOf(&s).Elem() + shouldPanic(func() { rv.Slice3(1, 2, 3) }) +} + +func TestSetLenCap(t *testing.T) { + xs := []int{1, 2, 3, 4, 5, 6, 7, 8} + xa := [8]int{10, 20, 30, 40, 50, 60, 70, 80} + + vs := ValueOf(&xs).Elem() + shouldPanic(func() { vs.SetLen(10) }) + shouldPanic(func() { vs.SetCap(10) }) + shouldPanic(func() { vs.SetLen(-1) }) + shouldPanic(func() { vs.SetCap(-1) }) + shouldPanic(func() { vs.SetCap(6) }) // smaller than len + vs.SetLen(5) + if len(xs) != 5 || cap(xs) != 8 { + t.Errorf("after SetLen(5), len, cap = %d, %d, want 5, 8", len(xs), cap(xs)) + } + vs.SetCap(6) + if len(xs) != 5 || cap(xs) != 6 { + t.Errorf("after SetCap(6), len, cap = %d, %d, want 5, 6", len(xs), cap(xs)) + } + vs.SetCap(5) + if len(xs) != 5 || cap(xs) != 5 { + t.Errorf("after SetCap(5), len, cap = %d, %d, want 5, 5", len(xs), cap(xs)) + } + shouldPanic(func() { vs.SetCap(4) }) // smaller than len + shouldPanic(func() { vs.SetLen(6) }) // bigger than cap + + va := ValueOf(&xa).Elem() + shouldPanic(func() { va.SetLen(8) }) + shouldPanic(func() { va.SetCap(8) }) +} + func TestVariadic(t *testing.T) { var b bytes.Buffer V := ValueOf @@ -2958,17 +3052,28 @@ func TestConvert(t *testing.T) { all[t2] = true canConvert[[2]Type{t1, t2}] = true + // vout1 represents the in value converted to the in type. v1 := tt.in vout1 := v1.Convert(t1) out1 := vout1.Interface() if vout1.Type() != tt.in.Type() || !DeepEqual(out1, tt.in.Interface()) { - t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t1, out1, out1, tt.in.Interface(), tt.in.Interface()) + t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t1, out1, tt.in.Interface()) + } + + // vout2 represents the in value converted to the out type. + vout2 := v1.Convert(t2) + out2 := vout2.Interface() + if vout2.Type() != tt.out.Type() || !DeepEqual(out2, tt.out.Interface()) { + t.Errorf("ValueOf(%T(%[1]v)).Convert(%s) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out2, tt.out.Interface()) } - vout := v1.Convert(t2) - out := vout.Interface() - if vout.Type() != tt.out.Type() || !DeepEqual(out, tt.out.Interface()) { - t.Errorf("ValueOf(%T(%v)).Convert(%s) = %T(%v), want %T(%v)", tt.in.Interface(), tt.in.Interface(), t2, out, out, tt.out.Interface(), tt.out.Interface()) + // vout3 represents a new value of the out type, set to vout2. This makes + // sure the converted value vout2 is really usable as a regular value. + vout3 := New(t2).Elem() + vout3.Set(vout2) + out3 := vout3.Interface() + if vout3.Type() != tt.out.Type() || !DeepEqual(out3, tt.out.Interface()) { + t.Errorf("Set(ValueOf(%T(%[1]v)).Convert(%s)) = %T(%[3]v), want %T(%[4]v)", tt.in.Interface(), t2, out3, tt.out.Interface()) } if IsRO(v1) { @@ -2977,8 +3082,11 @@ func TestConvert(t *testing.T) { if IsRO(vout1) { t.Errorf("self-conversion output %v is RO, should not be", vout1) } - if IsRO(vout) { - t.Errorf("conversion output %v is RO, should not be", vout) + if IsRO(vout2) { + t.Errorf("conversion output %v is RO, should not be", vout2) + } + if IsRO(vout3) { + t.Errorf("set(conversion output) %v is RO, should not be", vout3) } if !IsRO(MakeRO(v1).Convert(t1)) { t.Errorf("RO self-conversion output %v is not RO, should be", v1) @@ -3405,6 +3513,46 @@ func BenchmarkFieldByName3(b *testing.B) { } } +type S struct { + i1 int64 + i2 int64 +} + +func BenchmarkInterfaceBig(b *testing.B) { + v := ValueOf(S{}) + for i := 0; i < b.N; i++ { + v.Interface() + } + b.StopTimer() +} + +func TestAllocsInterfaceBig(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } + v := ValueOf(S{}) + if allocs := testing.AllocsPerRun(100, func() { v.Interface() }); allocs > 0 { + t.Error("allocs:", allocs) + } +} + +func BenchmarkInterfaceSmall(b *testing.B) { + v := ValueOf(int64(0)) + for i := 0; i < b.N; i++ { + v.Interface() + } +} + +func TestAllocsInterfaceSmall(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } + v := ValueOf(int64(0)) + if allocs := testing.AllocsPerRun(100, func() { v.Interface() }); allocs > 0 { + t.Error("allocs:", allocs) + } +} + // An exhaustive is a mechanism for writing exhaustive or stochastic tests. // The basic usage is: // diff --git a/libgo/go/reflect/deepequal.go b/libgo/go/reflect/deepequal.go index 915afed4..e3bf3dc 100644 --- a/libgo/go/reflect/deepequal.go +++ b/libgo/go/reflect/deepequal.go @@ -9,18 +9,17 @@ package reflect // During deepValueEqual, must keep track of checks that are // in progress. The comparison algorithm assumes that all // checks in progress are true when it reencounters them. -// Visited are stored in a map indexed by 17 * a1 + a2; +// Visited comparisons are stored in a map indexed by visit. type visit struct { - a1 uintptr - a2 uintptr - typ Type - next *visit + a1 uintptr + a2 uintptr + typ Type } // Tests for deep equality using reflected types. The map argument tracks // comparisons that have already been seen, which allows short circuiting on // recursive types. -func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool) { +func deepValueEqual(v1, v2 Value, visited map[visit]bool, depth int) bool { if !v1.IsValid() || !v2.IsValid() { return v1.IsValid() == v2.IsValid() } @@ -29,8 +28,15 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } // if depth > 10 { panic("deepValueEqual") } // for debugging + hard := func(k Kind) bool { + switch k { + case Array, Map, Slice, Struct: + return true + } + return false + } - if v1.CanAddr() && v2.CanAddr() { + if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) { addr1 := v1.UnsafeAddr() addr2 := v2.UnsafeAddr() if addr1 > addr2 { @@ -44,17 +50,14 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool } // ... or already seen - h := 17*addr1 + addr2 - seen := visited[h] typ := v1.Type() - for p := seen; p != nil; p = p.next { - if p.a1 == addr1 && p.a2 == addr2 && p.typ == typ { - return true - } + v := visit{addr1, addr2, typ} + if visited[v] { + return true } // Remember for later. - visited[h] = &visit{addr1, addr2, typ, seen} + visited[v] = true } switch v1.Kind() { @@ -75,6 +78,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool if v1.Len() != v2.Len() { return false } + if v1.Pointer() == v2.Pointer() { + return true + } for i := 0; i < v1.Len(); i++ { if !deepValueEqual(v1.Index(i), v2.Index(i), visited, depth+1) { return false @@ -102,6 +108,9 @@ func deepValueEqual(v1, v2 Value, visited map[uintptr]*visit, depth int) (b bool if v1.Len() != v2.Len() { return false } + if v1.Pointer() == v2.Pointer() { + return true + } for _, k := range v1.MapKeys() { if !deepValueEqual(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) { return false @@ -135,5 +144,5 @@ func DeepEqual(a1, a2 interface{}) bool { if v1.Type() != v2.Type() { return false } - return deepValueEqual(v1, v2, make(map[uintptr]*visit), 0) + return deepValueEqual(v1, v2, make(map[visit]bool), 0) } diff --git a/libgo/go/reflect/example_test.go b/libgo/go/reflect/example_test.go index 62455c0..cca28ee 100644 --- a/libgo/go/reflect/example_test.go +++ b/libgo/go/reflect/example_test.go @@ -50,3 +50,17 @@ func ExampleMakeFunc() { // 1 0 // 3.14 2.72 } + +func ExampleStructTag() { + type S struct { + F string `species:"gopher" color:"blue"` + } + + s := S{} + st := reflect.TypeOf(s) + field := st.Field(0) + fmt.Println(field.Tag.Get("color"), field.Tag.Get("species")) + + // Output: + // blue gopher +} diff --git a/libgo/go/reflect/makefunc.go b/libgo/go/reflect/makefunc.go index 3e8085b..505c543 100644 --- a/libgo/go/reflect/makefunc.go +++ b/libgo/go/reflect/makefunc.go @@ -23,7 +23,7 @@ type makeFuncImpl struct { // that wraps the function fn. When called, that new function // does the following: // -// - converts its arguments to a list of Values args. +// - converts its arguments to a slice of Values. // - runs results := fn(args). // - returns the results as a slice of Values, one per formal result. // diff --git a/libgo/go/reflect/type.go b/libgo/go/reflect/type.go index d084f38..aaac2c3 100644 --- a/libgo/go/reflect/type.go +++ b/libgo/go/reflect/type.go @@ -191,6 +191,14 @@ type Type interface { uncommon() *uncommonType } +// BUG(rsc): FieldByName and related functions consider struct field names to be equal +// if the names are equal, even if they are unexported names originating +// in different packages. The practical effect of this is that the result of +// t.FieldByName("x") is not well defined if the struct type t contains +// multiple fields named x (embedded from different packages). +// FieldByName may return one of the fields named x or may report that there are none. +// See golang.org/issue/4876 for more details. + /* * These data structures are known to the compiler (../../cmd/gc/reflect.c). * A few are known to ../runtime/type.go to convey to debuggers. @@ -320,6 +328,8 @@ type mapType struct { rtype `reflect:"map"` key *rtype // map key type elem *rtype // map element (value) type + // bucket *rtype // internal bucket structure + // hmap *rtype // internal map header } // ptrType represents a pointer type. @@ -358,7 +368,6 @@ const ( _GC_ARRAY_START _GC_ARRAY_NEXT _GC_CALL - _GC_MAP_PTR _GC_CHAN_PTR _GC_STRING _GC_EFACE @@ -1382,11 +1391,11 @@ func cachePut(k cacheKey, t *rtype) Type { return t } -// garbage collection bytecode program for chan or map. +// garbage collection bytecode program for chan. // See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. -type chanMapGC struct { +type chanGC struct { width uintptr // sizeof(map) - op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR + op uintptr // _GC_CHAN_PTR off uintptr // 0 typ *rtype // map type end uintptr // _GC_END @@ -1500,6 +1509,8 @@ func MapOf(key, elem Type) Type { mt.key = ktyp mt.elem = etyp + // mt.bucket = bucketOf(ktyp, etyp) + // mt.hmap = hMapOf(mt.bucket) mt.uncommonType = nil mt.ptrToThis = nil @@ -1510,6 +1521,118 @@ func MapOf(key, elem Type) Type { return cachePut(ckey, &mt.rtype) } +// Make sure these routines stay in sync with ../../pkg/runtime/hashmap.c! +// These types exist only for GC, so we only fill out GC relevant info. +// Currently, that's just size and the GC program. We also fill in string +// for possible debugging use. +const ( + _BUCKETSIZE = 8 + _MAXKEYSIZE = 128 + _MAXVALSIZE = 128 +) + +func bucketOf(ktyp, etyp *rtype) *rtype { + if ktyp.size > _MAXKEYSIZE { + ktyp = PtrTo(ktyp).(*rtype) + } + if etyp.size > _MAXVALSIZE { + etyp = PtrTo(etyp).(*rtype) + } + ptrsize := unsafe.Sizeof(uintptr(0)) + + gc := make([]uintptr, 1) // first entry is size, filled in at the end + offset := _BUCKETSIZE * unsafe.Sizeof(uint8(0)) // topbits + gc = append(gc, _GC_PTR, offset, 0 /*self pointer set below*/) // overflow + offset += ptrsize + + // keys + if ktyp.kind&kindNoPointers == 0 { + gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, ktyp.size) + gc = appendGCProgram(gc, ktyp) + gc = append(gc, _GC_ARRAY_NEXT) + } + offset += _BUCKETSIZE * ktyp.size + + // values + if etyp.kind&kindNoPointers == 0 { + gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, etyp.size) + gc = appendGCProgram(gc, etyp) + gc = append(gc, _GC_ARRAY_NEXT) + } + offset += _BUCKETSIZE * etyp.size + + gc = append(gc, _GC_END) + gc[0] = offset + gc[3] = uintptr(unsafe.Pointer(&gc[0])) // set self pointer + + b := new(rtype) + b.size = offset + // b.gc = unsafe.Pointer(&gc[0]) + s := "bucket(" + *ktyp.string + "," + *etyp.string + ")" + b.string = &s + return b +} + +// Take the GC program for "t" and append it to the GC program "gc". +func appendGCProgram(gc []uintptr, t *rtype) []uintptr { + // p := t.gc + var p unsafe.Pointer + p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size +loop: + for { + var argcnt int + switch *(*uintptr)(p) { + case _GC_END: + // Note: _GC_END not included in append + break loop + case _GC_ARRAY_NEXT: + argcnt = 0 + case _GC_APTR, _GC_STRING, _GC_EFACE, _GC_IFACE: + argcnt = 1 + case _GC_PTR, _GC_CALL, _GC_CHAN_PTR, _GC_SLICE: + argcnt = 2 + case _GC_ARRAY_START, _GC_REGION: + argcnt = 3 + default: + panic("unknown GC program op for " + *t.string + ": " + strconv.FormatUint(*(*uint64)(p), 10)) + } + for i := 0; i < argcnt+1; i++ { + gc = append(gc, *(*uintptr)(p)) + p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) + } + } + return gc +} +func hMapOf(bucket *rtype) *rtype { + ptrsize := unsafe.Sizeof(uintptr(0)) + + // make gc program & compute hmap size + gc := make([]uintptr, 1) // first entry is size, filled in at the end + offset := unsafe.Sizeof(uint(0)) // count + offset += unsafe.Sizeof(uint32(0)) // flags + offset += unsafe.Sizeof(uint32(0)) // hash0 + offset += unsafe.Sizeof(uint8(0)) // B + offset += unsafe.Sizeof(uint8(0)) // keysize + offset += unsafe.Sizeof(uint8(0)) // valuesize + offset = (offset + 1) / 2 * 2 + offset += unsafe.Sizeof(uint16(0)) // bucketsize + offset = (offset + ptrsize - 1) / ptrsize * ptrsize + // gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // buckets + offset += ptrsize + // gc = append(gc, _GC_PTR, offset, uintptr(bucket.gc)) // oldbuckets + offset += ptrsize + offset += ptrsize // nevacuate + gc = append(gc, _GC_END) + gc[0] = offset + + h := new(rtype) + h.size = offset + // h.gc = unsafe.Pointer(&gc[0]) + s := "hmap(" + *bucket.string + ")" + h.string = &s + return h +} + // garbage collection bytecode program for slice of non-zero-length values. // See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym. type sliceGC struct { diff --git a/libgo/go/reflect/value.go b/libgo/go/reflect/value.go index b199f70..216ee3f 100644 --- a/libgo/go/reflect/value.go +++ b/libgo/go/reflect/value.go @@ -561,47 +561,6 @@ func align(x, n uintptr) uintptr { return (x + n - 1) &^ (n - 1) } -// frameSize returns the sizes of the argument and result frame -// for a function of the given type. The rcvr bool specifies whether -// a one-word receiver should be included in the total. -func frameSize(t *rtype, rcvr bool) (total, in, outOffset, out uintptr) { - if rcvr { - // extra word for receiver interface word - total += ptrSize - } - - nin := t.NumIn() - in = -total - for i := 0; i < nin; i++ { - tv := t.In(i) - total = align(total, uintptr(tv.Align())) - total += tv.Size() - } - in += total - total = align(total, ptrSize) - nout := t.NumOut() - outOffset = total - out = -total - for i := 0; i < nout; i++ { - tv := t.Out(i) - total = align(total, uintptr(tv.Align())) - total += tv.Size() - } - out += total - - // total must be > 0 in order for &args[0] to be valid. - // the argument copying is going to round it up to - // a multiple of ptrSize anyway, so make it ptrSize to begin with. - if total < ptrSize { - total = ptrSize - } - - // round to pointer - total = align(total, ptrSize) - - return -} - // funcName returns the name of f, for use in error messages. func funcName(f func([]Value) []Value) string { pc := *(*uintptr)(unsafe.Pointer(&f)) @@ -894,10 +853,7 @@ func (v Value) CanInterface() bool { // Interface returns v's current value as an interface{}. // It is equivalent to: // var i interface{} = (v's underlying value) -// If v is a method obtained by invoking Value.Method -// (as opposed to Type.Method), Interface cannot return an -// interface value, so it panics. -// It also panics if the Value was obtained by accessing +// It panics if the Value was obtained by accessing // unexported struct fields. func (v Value) Interface() (i interface{}) { return valueInterface(v, true) @@ -935,7 +891,8 @@ func valueInterface(v Value, safe bool) interface{} { eface.typ = toType(v.typ).common() eface.word = v.iword() - if v.flag&flagIndir != 0 && v.kind() != Ptr && v.kind() != UnsafePointer { + // Don't need to allocate if v is not addressable or fits in one word. + if v.flag&flagAddr != 0 && v.kind() != Ptr && v.kind() != UnsafePointer { // eface.word is a pointer to the actual data, // which might be changed. We need to return // a pointer to unchanging data, so make a copy. @@ -1411,6 +1368,19 @@ func (v Value) SetLen(n int) { s.Len = n } +// SetCap sets v's capacity to n. +// It panics if v's Kind is not Slice or if n is smaller than the length or +// greater than the capacity of the slice. +func (v Value) SetCap(n int) { + v.mustBeAssignable() + v.mustBe(Slice) + s := (*SliceHeader)(v.val) + if n < int(s.Len) || n > int(s.Cap) { + panic("reflect: slice capacity out of range in SetCap") + } + s.Cap = n +} + // 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. @@ -1467,17 +1437,18 @@ func (v Value) SetString(x string) { *(*string)(v.val) = x } -// Slice returns a slice of v. -// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array. -func (v Value) Slice(beg, end int) Value { +// Slice returns v[i:j]. +// It panics if v's Kind is not Array, Slice or String, or if v is an unaddressable array, +// or if the indexes are out of bounds. +func (v Value) Slice(i, j int) Value { var ( cap int typ *sliceType base unsafe.Pointer ) - switch k := v.kind(); k { + switch kind := v.kind(); kind { default: - panic(&ValueError{"reflect.Value.Slice", k}) + panic(&ValueError{"reflect.Value.Slice", kind}) case Array: if v.flag&flagAddr == 0 { @@ -1496,17 +1467,17 @@ func (v Value) Slice(beg, end int) Value { case String: s := (*StringHeader)(v.val) - if beg < 0 || end < beg || end > s.Len { + if i < 0 || j < i || j > s.Len { panic("reflect.Value.Slice: string slice index out of bounds") } var x string val := (*StringHeader)(unsafe.Pointer(&x)) - val.Data = s.Data + uintptr(beg) - val.Len = end - beg + val.Data = s.Data + uintptr(i) + val.Len = j - i return Value{v.typ, unsafe.Pointer(&x), v.flag} } - if beg < 0 || end < beg || end > cap { + if i < 0 || j < i || j > cap { panic("reflect.Value.Slice: slice index out of bounds") } @@ -1515,9 +1486,56 @@ func (v Value) Slice(beg, end int) Value { // Reinterpret as *SliceHeader to edit. s := (*SliceHeader)(unsafe.Pointer(&x)) - s.Data = uintptr(base) + uintptr(beg)*typ.elem.Size() - s.Len = end - beg - s.Cap = cap - beg + s.Data = uintptr(base) + uintptr(i)*typ.elem.Size() + s.Len = j - i + s.Cap = cap - i + + fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift + return Value{typ.common(), unsafe.Pointer(&x), fl} +} + +// Slice3 is the 3-index form of the slice operation: it returns v[i:j:k]. +// It panics if v's Kind is not Array or Slice, or if v is an unaddressable array, +// or if the indexes are out of bounds. +func (v Value) Slice3(i, j, k int) Value { + var ( + cap int + typ *sliceType + base unsafe.Pointer + ) + switch kind := v.kind(); kind { + default: + panic(&ValueError{"reflect.Value.Slice3", kind}) + + case Array: + if v.flag&flagAddr == 0 { + panic("reflect.Value.Slice: slice of unaddressable array") + } + tt := (*arrayType)(unsafe.Pointer(v.typ)) + cap = int(tt.len) + typ = (*sliceType)(unsafe.Pointer(tt.slice)) + base = v.val + + case Slice: + typ = (*sliceType)(unsafe.Pointer(v.typ)) + s := (*SliceHeader)(v.val) + base = unsafe.Pointer(s.Data) + cap = s.Cap + } + + if i < 0 || j < i || k < j || k > cap { + panic("reflect.Value.Slice3: slice index out of bounds") + } + + // Declare slice so that the garbage collector + // can see the base pointer in it. + var x []unsafe.Pointer + + // Reinterpret as *SliceHeader to edit. + s := (*SliceHeader)(unsafe.Pointer(&x)) + s.Data = uintptr(base) + uintptr(i)*typ.elem.Size() + s.Len = j - i + s.Cap = k - i fl := v.flag&flagRO | flagIndir | flag(Slice)<<flagKindShift return Value{typ.common(), unsafe.Pointer(&x), fl} diff --git a/libgo/go/regexp/all_test.go b/libgo/go/regexp/all_test.go index 9c4d64f..e914a7c 100644 --- a/libgo/go/regexp/all_test.go +++ b/libgo/go/regexp/all_test.go @@ -308,14 +308,14 @@ func TestReplaceAllFunc(t *testing.T) { } actual := re.ReplaceAllStringFunc(tc.input, tc.replacement) if actual != tc.output { - t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q", - tc.pattern, tc.input, tc.replacement, actual, tc.output) + t.Errorf("%q.ReplaceFunc(%q,fn) = %q; want %q", + tc.pattern, tc.input, actual, tc.output) } // now try bytes actual = string(re.ReplaceAllFunc([]byte(tc.input), func(s []byte) []byte { return []byte(tc.replacement(string(s))) })) if actual != tc.output { - t.Errorf("%q.ReplaceFunc(%q,%q) = %q; want %q", - tc.pattern, tc.input, tc.replacement, actual, tc.output) + t.Errorf("%q.ReplaceFunc(%q,fn) = %q; want %q", + tc.pattern, tc.input, actual, tc.output) } } } diff --git a/libgo/go/regexp/exec2_test.go b/libgo/go/regexp/exec2_test.go new file mode 100644 index 0000000..7b86b41 --- /dev/null +++ b/libgo/go/regexp/exec2_test.go @@ -0,0 +1,20 @@ +// 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 !race + +package regexp + +import ( + "testing" +) + +// This test is excluded when running under the race detector because +// it is a very expensive test and takes too long. +func TestRE2Exhaustive(t *testing.T) { + if testing.Short() { + t.Skip("skipping TestRE2Exhaustive during short test") + } + testRE2(t, "testdata/re2-exhaustive.txt.bz2") +} diff --git a/libgo/go/regexp/exec_test.go b/libgo/go/regexp/exec_test.go index 9dfaed7..70d069c 100644 --- a/libgo/go/regexp/exec_test.go +++ b/libgo/go/regexp/exec_test.go @@ -9,7 +9,6 @@ import ( "compress/bzip2" "fmt" "io" - "math/rand" "os" "path/filepath" "regexp/syntax" @@ -67,13 +66,6 @@ func TestRE2Search(t *testing.T) { testRE2(t, "testdata/re2-search.txt") } -func TestRE2Exhaustive(t *testing.T) { - if testing.Short() { - t.Skip("skipping TestRE2Exhaustive during short test") - } - testRE2(t, "testdata/re2-exhaustive.txt.bz2") -} - func testRE2(t *testing.T, file string) { f, err := os.Open(file) if err != nil { @@ -650,11 +642,17 @@ func makeText(n int) []byte { return text[:n] } text = make([]byte, n) + x := ^uint32(0) for i := range text { - if rand.Intn(30) == 0 { + x += x + x ^= 1 + if int32(x) < 0 { + x ^= 0x88888eef + } + if x%31 == 0 { text[i] = '\n' } else { - text[i] = byte(rand.Intn(0x7E+1-0x20) + 0x20) + text[i] = byte(x%(0x7E+1-0x20) + 0x20) } } return text @@ -691,7 +689,7 @@ func BenchmarkMatchEasy1_1K(b *testing.B) { benchmark(b, easy1, 1<<10) } func BenchmarkMatchEasy1_32K(b *testing.B) { benchmark(b, easy1, 32<<10) } func BenchmarkMatchEasy1_1M(b *testing.B) { benchmark(b, easy1, 1<<20) } func BenchmarkMatchEasy1_32M(b *testing.B) { benchmark(b, easy1, 32<<20) } -func BenchmarkMatchMedium_32(b *testing.B) { benchmark(b, medium, 1<<0) } +func BenchmarkMatchMedium_32(b *testing.B) { benchmark(b, medium, 32<<0) } func BenchmarkMatchMedium_1K(b *testing.B) { benchmark(b, medium, 1<<10) } func BenchmarkMatchMedium_32K(b *testing.B) { benchmark(b, medium, 32<<10) } func BenchmarkMatchMedium_1M(b *testing.B) { benchmark(b, medium, 1<<20) } diff --git a/libgo/go/regexp/regexp.go b/libgo/go/regexp/regexp.go index c392b37..0046026 100644 --- a/libgo/go/regexp/regexp.go +++ b/libgo/go/regexp/regexp.go @@ -375,21 +375,18 @@ func (re *Regexp) LiteralPrefix() (prefix string, complete bool) { return re.prefix, re.prefixComplete } -// MatchReader returns whether the Regexp matches the text read by the -// RuneReader. The return value is a boolean: true for match, false for no -// match. +// MatchReader reports whether the Regexp matches the text read by the +// RuneReader. func (re *Regexp) MatchReader(r io.RuneReader) bool { return re.doExecute(r, nil, "", 0, 0) != nil } -// MatchString returns whether the Regexp matches the string s. -// The return value is a boolean: true for match, false for no match. +// MatchString reports whether the Regexp matches the string s. func (re *Regexp) MatchString(s string) bool { return re.doExecute(nil, nil, s, 0, 0) != nil } -// Match returns whether the Regexp matches the byte slice b. -// The return value is a boolean: true for match, false for no match. +// Match reports whether the Regexp matches the byte slice b. func (re *Regexp) Match(b []byte) bool { return re.doExecute(nil, b, "", 0, 0) != nil } diff --git a/libgo/go/regexp/syntax/doc.go b/libgo/go/regexp/syntax/doc.go index bcb5d05..e52632e 100644 --- a/libgo/go/regexp/syntax/doc.go +++ b/libgo/go/regexp/syntax/doc.go @@ -64,8 +64,8 @@ Empty strings: ^ at beginning of text or line (flag m=true) $ at end of text (like \z not \Z) or line (flag m=true) \A at beginning of text - \b at word boundary (\w on one side and \W, \A, or \z on the other) - \B not a word boundary + \b at ASCII word boundary (\w on one side and \W, \A, or \z on the other) + \B not an ASCII word boundary \z at end of text Escape sequences: @@ -104,8 +104,8 @@ Perl character classes: \D not digits (== [^0-9]) \s whitespace (== [\t\n\f\r ]) \S not whitespace (== [^\t\n\f\r ]) - \w word characters (== [0-9A-Za-z_]) - \W not word characters (== [^0-9A-Za-z_]) + \w ASCII word characters (== [0-9A-Za-z_]) + \W not ASCII word characters (== [^0-9A-Za-z_]) ASCII character classes: [:alnum:] alphanumeric (== [0-9A-Za-z]) diff --git a/libgo/go/regexp/syntax/parse.go b/libgo/go/regexp/syntax/parse.go index 30e0e8b..42d0bf4 100644 --- a/libgo/go/regexp/syntax/parse.go +++ b/libgo/go/regexp/syntax/parse.go @@ -651,7 +651,7 @@ func literalRegexp(s string, flags Flags) *Regexp { // Parse parses a regular expression string s, controlled by the specified // Flags, and returns a regular expression parse tree. The syntax is -// described in the top-level comment for package regexp. +// described in the top-level comment. func Parse(s string, flags Flags) (*Regexp, error) { if flags&Literal != 0 { // Trivial parser for literal string. diff --git a/libgo/go/regexp/syntax/parse_test.go b/libgo/go/regexp/syntax/parse_test.go index 81fd9dc..269d6c3 100644 --- a/libgo/go/regexp/syntax/parse_test.go +++ b/libgo/go/regexp/syntax/parse_test.go @@ -542,7 +542,7 @@ func TestToStringEquivalentParse(t *testing.T) { // but "{" is a shorter equivalent in some contexts. nre, err := Parse(s, testFlags) if err != nil { - t.Errorf("Parse(%#q.String() = %#q): %v", tt.Regexp, t, err) + t.Errorf("Parse(%#q.String() = %#q): %v", tt.Regexp, s, err) continue } nd := dump(nre) diff --git a/libgo/go/regexp/syntax/prog.go b/libgo/go/regexp/syntax/prog.go index 902d3b3..a482a82 100644 --- a/libgo/go/regexp/syntax/prog.go +++ b/libgo/go/regexp/syntax/prog.go @@ -56,23 +56,26 @@ const ( // Passing r2 == -1 indicates that the position is // at the end of the text. func EmptyOpContext(r1, r2 rune) EmptyOp { - var op EmptyOp - if r1 < 0 { - op |= EmptyBeginText | EmptyBeginLine - } - if r1 == '\n' { + var op EmptyOp = EmptyNoWordBoundary + var boundary byte + switch { + case IsWordChar(r1): + boundary = 1 + case r1 == '\n': op |= EmptyBeginLine + case r1 < 0: + op |= EmptyBeginText | EmptyBeginLine } - if r2 < 0 { - op |= EmptyEndText | EmptyEndLine - } - if r2 == '\n' { + switch { + case IsWordChar(r2): + boundary ^= 1 + case r2 == '\n': op |= EmptyEndLine + case r2 < 0: + op |= EmptyEndText | EmptyEndLine } - if IsWordChar(r1) != IsWordChar(r2) { - op |= EmptyWordBoundary - } else { - op |= EmptyNoWordBoundary + if boundary != 0 { // IsWordChar(r1) != IsWordChar(r2) + op ^= (EmptyWordBoundary | EmptyNoWordBoundary) } return op } diff --git a/libgo/go/regexp/syntax/prog_test.go b/libgo/go/regexp/syntax/prog_test.go index 663d5a8..cd71abc 100644 --- a/libgo/go/regexp/syntax/prog_test.go +++ b/libgo/go/regexp/syntax/prog_test.go @@ -103,3 +103,14 @@ func TestCompile(t *testing.T) { } } } + +func BenchmarkEmptyOpContext(b *testing.B) { + for i := 0; i < b.N; i++ { + var r1 rune = -1 + for _, r2 := range "foo, bar, baz\nsome input text.\n" { + EmptyOpContext(r1, r2) + r1 = r2 + } + EmptyOpContext(r1, -1) + } +} diff --git a/libgo/go/runtime/append_test.go b/libgo/go/runtime/append_test.go index 3639018..937c825 100644 --- a/libgo/go/runtime/append_test.go +++ b/libgo/go/runtime/append_test.go @@ -38,10 +38,18 @@ func BenchmarkAppend4Bytes(b *testing.B) { benchmarkAppendBytes(b, 4) } +func BenchmarkAppend7Bytes(b *testing.B) { + benchmarkAppendBytes(b, 7) +} + func BenchmarkAppend8Bytes(b *testing.B) { benchmarkAppendBytes(b, 8) } +func BenchmarkAppend15Bytes(b *testing.B) { + benchmarkAppendBytes(b, 15) +} + func BenchmarkAppend16Bytes(b *testing.B) { benchmarkAppendBytes(b, 16) } @@ -121,3 +129,43 @@ func TestAppendOverlap(t *testing.T) { t.Errorf("overlap failed: got %q want %q", got, want) } } + +func benchmarkCopySlice(b *testing.B, l int) { + s := make([]byte, l) + buf := make([]byte, 4096) + var n int + for i := 0; i < b.N; i++ { + n = copy(buf, s) + } + b.SetBytes(int64(n)) +} + +func benchmarkCopyStr(b *testing.B, l int) { + s := string(make([]byte, l)) + buf := make([]byte, 4096) + var n int + for i := 0; i < b.N; i++ { + n = copy(buf, s) + } + b.SetBytes(int64(n)) +} + +func BenchmarkCopy1Byte(b *testing.B) { benchmarkCopySlice(b, 1) } +func BenchmarkCopy2Byte(b *testing.B) { benchmarkCopySlice(b, 2) } +func BenchmarkCopy4Byte(b *testing.B) { benchmarkCopySlice(b, 4) } +func BenchmarkCopy8Byte(b *testing.B) { benchmarkCopySlice(b, 8) } +func BenchmarkCopy12Byte(b *testing.B) { benchmarkCopySlice(b, 12) } +func BenchmarkCopy16Byte(b *testing.B) { benchmarkCopySlice(b, 16) } +func BenchmarkCopy32Byte(b *testing.B) { benchmarkCopySlice(b, 32) } +func BenchmarkCopy128Byte(b *testing.B) { benchmarkCopySlice(b, 128) } +func BenchmarkCopy1024Byte(b *testing.B) { benchmarkCopySlice(b, 1024) } + +func BenchmarkCopy1String(b *testing.B) { benchmarkCopyStr(b, 1) } +func BenchmarkCopy2String(b *testing.B) { benchmarkCopyStr(b, 2) } +func BenchmarkCopy4String(b *testing.B) { benchmarkCopyStr(b, 4) } +func BenchmarkCopy8String(b *testing.B) { benchmarkCopyStr(b, 8) } +func BenchmarkCopy12String(b *testing.B) { benchmarkCopyStr(b, 12) } +func BenchmarkCopy16String(b *testing.B) { benchmarkCopyStr(b, 16) } +func BenchmarkCopy32String(b *testing.B) { benchmarkCopyStr(b, 32) } +func BenchmarkCopy128String(b *testing.B) { benchmarkCopyStr(b, 128) } +func BenchmarkCopy1024String(b *testing.B) { benchmarkCopyStr(b, 1024) } diff --git a/libgo/go/runtime/crash_cgo_test.go b/libgo/go/runtime/crash_cgo_test.go index 6b93bd1..b534b89 100644 --- a/libgo/go/runtime/crash_cgo_test.go +++ b/libgo/go/runtime/crash_cgo_test.go @@ -7,6 +7,7 @@ package runtime_test import ( + "runtime" "testing" ) @@ -15,13 +16,23 @@ func TestCgoCrashHandler(t *testing.T) { } func TestCgoSignalDeadlock(t *testing.T) { - /* gccgo does not have a go command + if testing.Short() && runtime.GOOS == "windows" { + t.Skip("Skipping in short mode") // takes up to 64 seconds + } + t.Skip("gccgo does not have a go command") got := executeTest(t, cgoSignalDeadlockSource, nil) want := "OK\n" if got != want { t.Fatalf("expected %q, but got %q", want, got) } - */ +} + +func TestCgoTraceback(t *testing.T) { + got := executeTest(t, cgoTracebackSource, nil) + want := "OK\n" + if got != want { + t.Fatalf("expected %q, but got %q", want, got) + } } const cgoSignalDeadlockSource = ` @@ -88,3 +99,22 @@ func main() { fmt.Printf("OK\n") } ` + +const cgoTracebackSource = ` +package main + +/* void foo(void) {} */ +import "C" + +import ( + "fmt" + "runtime" +) + +func main() { + C.foo() + buf := make([]byte, 1) + runtime.Stack(buf, true) + fmt.Printf("OK\n") +} +` diff --git a/libgo/go/runtime/crash_test.go b/libgo/go/runtime/crash_test.go index 918249a..d8bfdbd 100644 --- a/libgo/go/runtime/crash_test.go +++ b/libgo/go/runtime/crash_test.go @@ -14,7 +14,7 @@ import ( "text/template" ) -// testEnv excludes GOGCTRACE from the environment +// testEnv excludes GODEBUG from the environment // to prevent its output from breaking tests that // are trying to parse other command output. func testEnv(cmd *exec.Cmd) *exec.Cmd { @@ -22,7 +22,7 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd { panic("environment already set") } for _, env := range os.Environ() { - if strings.HasPrefix(env, "GOGCTRACE=") { + if strings.HasPrefix(env, "GODEBUG=") { continue } cmd.Env = append(cmd.Env, env) @@ -31,6 +31,7 @@ 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") checkStaleRuntime(t) st := template.Must(template.New("crashSource").Parse(templ)) @@ -44,14 +45,16 @@ func executeTest(t *testing.T, templ string, data interface{}) string { src := filepath.Join(dir, "main.go") f, err := os.Create(src) if err != nil { - t.Fatalf("failed to create %v: %v", src, err) + t.Fatalf("failed to create file: %v", err) } err = st.Execute(f, data) if err != nil { f.Close() t.Fatalf("failed to execute template: %v", err) } - f.Close() + if err := f.Close(); err != nil { + t.Fatalf("failed to close file: %v", err) + } got, _ := testEnv(exec.Command("go", "run", src)).CombinedOutput() return string(got) @@ -69,16 +72,14 @@ func checkStaleRuntime(t *testing.T) { } func testCrashHandler(t *testing.T, cgo bool) { - /* gccgo does not have a go command type crashTest struct { Cgo bool } - got := executeTest(t, crashSource, &crashTest{Cgo: cgo}) + output := executeTest(t, crashSource, &crashTest{Cgo: cgo}) want := "main: recovered done\nnew-thread: recovered done\nsecond-new-thread: recovered done\nmain-again: recovered done\n" - if got != want { - t.Fatalf("expected %q, but got %q", want, got) + if output != want { + t.Fatalf("output:\n%s\n\nwanted:\n%s", output, want) } - */ } func TestCrashHandler(t *testing.T) { @@ -86,13 +87,11 @@ func TestCrashHandler(t *testing.T) { } func testDeadlock(t *testing.T, source string) { - /* gccgo does not have a go command. - got := executeTest(t, source, nil) + output := executeTest(t, source, nil) want := "fatal error: all goroutines are asleep - deadlock!\n" - if !strings.HasPrefix(got, want) { - t.Fatalf("expected %q, but got %q", want, got) + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) } - */ } func TestSimpleDeadlock(t *testing.T) { @@ -112,13 +111,26 @@ func TestLockedDeadlock2(t *testing.T) { } func TestGoexitDeadlock(t *testing.T) { - /* gccgo does not have a go command - got := executeTest(t, goexitDeadlockSource, nil) - want := "" - if got != want { - t.Fatalf("expected %q, but got %q", want, got) + output := executeTest(t, goexitDeadlockSource, nil) + if output != "" { + t.Fatalf("expected no output, got:\n%s", output) + } +} + +func TestStackOverflow(t *testing.T) { + output := executeTest(t, stackOverflowSource, nil) + want := "runtime: goroutine stack exceeds 4194304-byte limit\nfatal error: stack overflow" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) + } +} + +func TestThreadExhaustion(t *testing.T) { + output := executeTest(t, threadExhaustionSource, nil) + want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion" + if !strings.HasPrefix(output, want) { + t.Fatalf("output does not start with %q:\n%s", want, output) } - */ } const crashSource = ` @@ -223,3 +235,41 @@ func main() { runtime.Goexit() } ` + +const stackOverflowSource = ` +package main + +import "runtime/debug" + +func main() { + debug.SetMaxStack(4<<20) + f(make([]byte, 10)) +} + +func f(x []byte) byte { + var buf [64<<10]byte + return x[0] + f(buf[:]) +} +` + +const threadExhaustionSource = ` +package main + +import ( + "runtime" + "runtime/debug" +) + +func main() { + debug.SetMaxThreads(10) + c := make(chan int) + for i := 0; i < 100; i++ { + go func() { + runtime.LockOSThread() + c <- 0 + select{} + }() + <-c + } +} +` diff --git a/libgo/go/runtime/debug/garbage.go b/libgo/go/runtime/debug/garbage.go index 8f30264..8337d5d 100644 --- a/libgo/go/runtime/debug/garbage.go +++ b/libgo/go/runtime/debug/garbage.go @@ -24,6 +24,8 @@ func readGCStats(*[]time.Duration) func enableGC(bool) bool func setGCPercent(int) int func freeOSMemory() +func setMaxStack(int) int +func setMaxThreads(int) int // ReadGCStats reads statistics about garbage collection into stats. // The number of entries in the pause history is system-dependent; @@ -99,3 +101,35 @@ func SetGCPercent(percent int) int { func FreeOSMemory() { freeOSMemory() } + +// SetMaxStack sets the maximum amount of memory that +// can be used by a single goroutine stack. +// If any goroutine exceeds this limit while growing its stack, +// the program crashes. +// SetMaxStack returns the previous setting. +// The initial setting is 1 GB on 64-bit systems, 250 MB on 32-bit systems. +// +// SetMaxStack is useful mainly for limiting the damage done by +// goroutines that enter an infinite recursion. It only limits future +// stack growth. +func SetMaxStack(bytes int) int { + return setMaxStack(bytes) +} + +// SetMaxThreads sets the maximum number of operating system +// threads that the Go program can use. If it attempts to use more than +// this many, the program crashes. +// SetMaxThreads returns the previous setting. +// The initial setting is 10,000 threads. +// +// The limit controls the number of operating system threads, not the number +// of goroutines. A Go program creates a new thread only when a goroutine +// is ready to run but all the existing threads are blocked in system calls, cgo calls, +// or are locked to other goroutines due to use of runtime.LockOSThread. +// +// SetMaxThreads is useful mainly for limiting the damage done by +// programs that create an unbounded number of threads. The idea is +// to take down the program before it takes down the operating system. +func SetMaxThreads(threads int) int { + return setMaxThreads(threads) +} diff --git a/libgo/go/runtime/debug/stack_test.go b/libgo/go/runtime/debug/stack_test.go index bbd6626..263d715 100644 --- a/libgo/go/runtime/debug/stack_test.go +++ b/libgo/go/runtime/debug/stack_test.go @@ -49,10 +49,10 @@ func TestStack(t *testing.T) { n++ } } - frame("src/pkg/runtime/debug/stack_test.go", "\t(*T).ptrmethod: return Stack()") - frame("src/pkg/runtime/debug/stack_test.go", "\tT.method: return t.ptrmethod()") - frame("src/pkg/runtime/debug/stack_test.go", "\tTestStack: b := T(0).method()") - frame("src/pkg/testing/testing.go", "") + frame("stack_test.go", "\tmethod.N15_runtime_debug.T: return Stack()") + frame("stack_test.go", "\tmethod.N15_runtime_debug.T: return t.ptrmethod()") + frame("stack_test.go", "\tTestStack: b := T(0).method()") + frame("testing/testing.go", "") } func check(t *testing.T, line, has string) { diff --git a/libgo/go/runtime/error.go b/libgo/go/runtime/error.go index f7f81e9..88d5df5 100644 --- a/libgo/go/runtime/error.go +++ b/libgo/go/runtime/error.go @@ -106,6 +106,22 @@ func NewErrorString(s string, ret *interface{}) { *ret = errorString(s) } +// An errorCString represents a runtime error described by a single C string. +type errorCString uintptr + +func (e errorCString) RuntimeError() {} + +func cstringToGo(uintptr) string + +func (e errorCString) Error() string { + return "runtime error: " + cstringToGo(uintptr(e)) +} + +// For calling from C. +func NewErrorCString(s uintptr, ret *interface{}) { + *ret = errorCString(s) +} + type stringer interface { String() string } diff --git a/libgo/go/runtime/export_test.go b/libgo/go/runtime/export_test.go index e22fa62..2f678b6 100644 --- a/libgo/go/runtime/export_test.go +++ b/libgo/go/runtime/export_test.go @@ -65,3 +65,20 @@ func testSchedLocalQueueSteal() var TestSchedLocalQueue1 = testSchedLocalQueue var TestSchedLocalQueueSteal1 = testSchedLocalQueueSteal + +// func haveGoodHash() bool +// func stringHash(s string, seed uintptr) uintptr +// func bytesHash(b []byte, seed uintptr) uintptr +// func int32Hash(i uint32, seed uintptr) uintptr +// func int64Hash(i uint64, seed uintptr) uintptr + +// var HaveGoodHash = haveGoodHash +// var StringHash = stringHash +// var BytesHash = bytesHash +// var Int32Hash = int32Hash +// var Int64Hash = int64Hash + +// func GogoBytes() int32 + +var hashLoad float64 // declared in hashmap.c +var HashLoad = &hashLoad diff --git a/libgo/go/runtime/extern.go b/libgo/go/runtime/extern.go index 72e1a0a..527e9cd 100644 --- a/libgo/go/runtime/extern.go +++ b/libgo/go/runtime/extern.go @@ -21,11 +21,20 @@ is GOGC=100. Setting GOGC=off disables the garbage collector entirely. The runtime/debug package's SetGCPercent function allows changing this percentage at run time. See http://golang.org/pkg/runtime/debug/#SetGCPercent. -The GOGCTRACE variable controls debug output from the garbage collector. -Setting GOGCTRACE=1 causes the garbage collector to emit a single line to standard -error at each collection, summarizing the amount of memory collected and the -length of the pause. Setting GOGCTRACE=2 emits the same summary but also -repeats each collection. +The GODEBUG variable controls debug output from the runtime. GODEBUG value is +a comma-separated list of name=val pairs. Supported names are: + + gctrace: setting gctrace=1 causes the garbage collector to emit a single line to standard + error at each collection, summarizing the amount of memory collected and the + length of the pause. Setting gctrace=2 emits the same summary but also + repeats each collection. + + schedtrace: setting schedtrace=X causes the scheduler to emit a single line to standard + error every X milliseconds, summarizing the scheduler state. + + 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. The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads @@ -77,9 +86,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool) // It returns the number of entries written to pc. func Callers(skip int, pc []uintptr) int -type Func struct { // Keep in sync with runtime.h:struct Func - name string - entry uintptr // entry pc +type Func struct { + opaque struct{} // unexported field to disallow conversions } // FuncForPC returns a *Func describing the function that contains the @@ -87,10 +95,14 @@ type Func struct { // Keep in sync with runtime.h:struct Func func FuncForPC(pc uintptr) *Func // Name returns the name of the function. -func (f *Func) Name() string { return f.name } +func (f *Func) Name() string { + return funcname_go(f) +} // Entry returns the entry address of the function. -func (f *Func) Entry() uintptr { return f.entry } +func (f *Func) Entry() uintptr { + return funcentry_go(f) +} // FileLine returns the file name and line number of the // source code corresponding to the program counter pc. @@ -102,6 +114,8 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) { // implemented in symtab.c func funcline_go(*Func, uintptr) (string, int) +func funcname_go(*Func) string +func funcentry_go(*Func) uintptr // SetFinalizer sets the finalizer associated with x to f. // When the garbage collector finds an unreachable block @@ -116,8 +130,9 @@ func funcline_go(*Func, uintptr) (string, int) // The argument x must be a pointer to an object allocated by // calling new or by taking the address of a composite literal. // The argument f must be a function that takes a single argument -// of x's type and can have arbitrary ignored return values. -// If either of these is not true, SetFinalizer aborts the program. +// to which x's type can be assigned, and can have arbitrary ignored return +// values. If either of these is not true, SetFinalizer aborts the +// program. // // Finalizers are run in dependency order: if A points at B, both have // finalizers, and they are otherwise unreachable, only the finalizer diff --git a/libgo/go/runtime/gc_test.go b/libgo/go/runtime/gc_test.go index 05ee348..1b3ccbf 100644 --- a/libgo/go/runtime/gc_test.go +++ b/libgo/go/runtime/gc_test.go @@ -138,7 +138,9 @@ func TestGcRescan(t *testing.T) { for i := 0; i < 10; i++ { p := &Y{} p.c = make(chan error) - p.nextx = &head.X + if head != nil { + p.nextx = &head.X + } p.nexty = head p.p = new(int) *p.p = 42 diff --git a/libgo/go/runtime/malloc_test.go b/libgo/go/runtime/malloc_test.go new file mode 100644 index 0000000..054f6a7 --- /dev/null +++ b/libgo/go/runtime/malloc_test.go @@ -0,0 +1,156 @@ +// 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 runtime_test + +import ( + "flag" + . "runtime" + "testing" + "time" + "unsafe" +) + +func TestMemStats(t *testing.T) { + // Test that MemStats has sane values. + st := new(MemStats) + ReadMemStats(st) + if st.HeapSys == 0 || /* st.StackSys == 0 || */ st.MSpanSys == 0 || st.MCacheSys == 0 || + st.BuckHashSys == 0 || st.GCSys == 0 || st.OtherSys == 0 { + t.Fatalf("Zero sys value: %+v", *st) + } + if st.Sys != st.HeapSys+st.StackSys+st.MSpanSys+st.MCacheSys+ + st.BuckHashSys+st.GCSys+st.OtherSys { + t.Fatalf("Bad sys value: %+v", *st) + } +} + +var mallocSink uintptr + +func BenchmarkMalloc8(b *testing.B) { + var x uintptr + for i := 0; i < b.N; i++ { + p := new(int64) + x ^= uintptr(unsafe.Pointer(p)) + } + mallocSink = x +} + +func BenchmarkMalloc16(b *testing.B) { + var x uintptr + for i := 0; i < b.N; i++ { + p := new([2]int64) + x ^= uintptr(unsafe.Pointer(p)) + } + mallocSink = x +} + +func BenchmarkMallocTypeInfo8(b *testing.B) { + var x uintptr + for i := 0; i < b.N; i++ { + p := new(struct { + p [8 / unsafe.Sizeof(uintptr(0))]*int + }) + x ^= uintptr(unsafe.Pointer(p)) + } + mallocSink = x +} + +func BenchmarkMallocTypeInfo16(b *testing.B) { + var x uintptr + for i := 0; i < b.N; i++ { + p := new(struct { + p [16 / unsafe.Sizeof(uintptr(0))]*int + }) + x ^= uintptr(unsafe.Pointer(p)) + } + mallocSink = x +} + +var n = flag.Int("n", 1000, "number of goroutines") + +func BenchmarkGoroutineSelect(b *testing.B) { + quit := make(chan struct{}) + read := func(ch chan struct{}) { + for { + select { + case _, ok := <-ch: + if !ok { + return + } + case <-quit: + return + } + } + } + benchHelper(b, *n, read) +} + +func BenchmarkGoroutineBlocking(b *testing.B) { + read := func(ch chan struct{}) { + for { + if _, ok := <-ch; !ok { + return + } + } + } + benchHelper(b, *n, read) +} + +func BenchmarkGoroutineForRange(b *testing.B) { + read := func(ch chan struct{}) { + for _ = range ch { + } + } + benchHelper(b, *n, read) +} + +func benchHelper(b *testing.B, n int, read func(chan struct{})) { + m := make([]chan struct{}, n) + for i := range m { + m[i] = make(chan struct{}, 1) + go read(m[i]) + } + b.StopTimer() + b.ResetTimer() + GC() + + for i := 0; i < b.N; i++ { + for _, ch := range m { + if ch != nil { + ch <- struct{}{} + } + } + time.Sleep(10 * time.Millisecond) + b.StartTimer() + GC() + b.StopTimer() + } + + for _, ch := range m { + close(ch) + } + time.Sleep(10 * time.Millisecond) +} + +func BenchmarkGoroutineIdle(b *testing.B) { + quit := make(chan struct{}) + fn := func() { + <-quit + } + for i := 0; i < *n; i++ { + go fn() + } + + GC() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + GC() + } + + b.StopTimer() + close(quit) + time.Sleep(10 * time.Millisecond) +} diff --git a/libgo/go/runtime/map_test.go b/libgo/go/runtime/map_test.go index 535c59e..c53066a 100644 --- a/libgo/go/runtime/map_test.go +++ b/libgo/go/runtime/map_test.go @@ -378,3 +378,41 @@ func testMapLookups(t *testing.T, m map[string]string) { } } } + +// Tests whether the iterator returns the right elements when +// started in the middle of a grow, when the keys are NaNs. +func TestMapNanGrowIterator(t *testing.T) { + m := make(map[float64]int) + nan := math.NaN() + const nBuckets = 16 + // To fill nBuckets buckets takes LOAD * nBuckets keys. + nKeys := int(nBuckets * *runtime.HashLoad) + + // Get map to full point with nan keys. + for i := 0; i < nKeys; i++ { + m[nan] = i + } + // Trigger grow + m[1.0] = 1 + delete(m, 1.0) + + // Run iterator + found := make(map[int]struct{}) + for _, v := range m { + if v != -1 { + if _, repeat := found[v]; repeat { + t.Fatalf("repeat of value %d", v) + } + found[v] = struct{}{} + } + if len(found) == nKeys/2 { + // Halfway through iteration, finish grow. + for i := 0; i < nBuckets; i++ { + delete(m, 1.0) + } + } + } + if len(found) != nKeys { + t.Fatalf("missing value") + } +} diff --git a/libgo/go/runtime/mapspeed_test.go b/libgo/go/runtime/mapspeed_test.go index 3b7fbfd..d643d98 100644 --- a/libgo/go/runtime/mapspeed_test.go +++ b/libgo/go/runtime/mapspeed_test.go @@ -32,6 +32,33 @@ func BenchmarkHashStringSpeed(b *testing.B) { } } +type chunk [17]byte + +func BenchmarkHashBytesSpeed(b *testing.B) { + // a bunch of chunks, each with a different alignment mod 16 + var chunks [size]chunk + // initialize each to a different value + for i := 0; i < size; i++ { + chunks[i][0] = byte(i) + } + // put into a map + m := make(map[chunk]int, size) + for i, c := range chunks { + m[c] = i + } + idx := 0 + b.ResetTimer() + for i := 0; i < b.N; i++ { + if m[chunks[idx]] != idx { + b.Error("bad map entry for chunk") + } + idx++ + if idx == size { + idx = 0 + } + } +} + func BenchmarkHashInt32Speed(b *testing.B) { ints := make([]int32, size) for i := 0; i < size; i++ { @@ -206,3 +233,38 @@ func BenchmarkNewEmptyMap(b *testing.B) { _ = make(map[int]int) } } + +func BenchmarkMapIter(b *testing.B) { + m := make(map[int]bool) + for i := 0; i < 8; i++ { + m[i] = true + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, _ = range m { + } + } +} + +func BenchmarkMapIterEmpty(b *testing.B) { + m := make(map[int]bool) + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, _ = range m { + } + } +} + +func BenchmarkSameLengthMap(b *testing.B) { + // long strings, same length, differ in first few + // and last few bytes. + m := make(map[string]bool) + s1 := "foo" + strings.Repeat("-", 100) + "bar" + s2 := "goo" + strings.Repeat("-", 100) + "ber" + m[s1] = true + m[s2] = true + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = m[s1] + } +} diff --git a/libgo/go/runtime/mem.go b/libgo/go/runtime/mem.go index fd423c8..ba6d1cf 100644 --- a/libgo/go/runtime/mem.go +++ b/libgo/go/runtime/mem.go @@ -14,7 +14,7 @@ type MemStats struct { // General statistics. Alloc uint64 // bytes allocated and still in use TotalAlloc uint64 // bytes allocated (even if freed) - Sys uint64 // bytes obtained from system (should be sum of XxxSys below) + Sys uint64 // bytes obtained from system (sum of XxxSys below) Lookups uint64 // number of pointer lookups Mallocs uint64 // number of mallocs Frees uint64 // number of frees @@ -37,6 +37,8 @@ type MemStats struct { MCacheInuse uint64 // mcache structures MCacheSys uint64 BuckHashSys uint64 // profiling bucket hash table + GCSys uint64 // GC metadata + OtherSys uint64 // other system allocations // Garbage collector statistics. NextGC uint64 // next run in HeapAlloc time (bytes) diff --git a/libgo/go/runtime/memmove_test.go b/libgo/go/runtime/memmove_test.go new file mode 100644 index 0000000..9525f06 --- /dev/null +++ b/libgo/go/runtime/memmove_test.go @@ -0,0 +1,116 @@ +// 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 runtime_test + +import ( + "testing" +) + +func TestMemmove(t *testing.T) { + size := 256 + if testing.Short() { + size = 128 + 16 + } + src := make([]byte, size) + dst := make([]byte, size) + for i := 0; i < size; i++ { + src[i] = byte(128 + (i & 127)) + } + for i := 0; i < size; i++ { + dst[i] = byte(i & 127) + } + for n := 0; n <= size; n++ { + for x := 0; x <= size-n; x++ { // offset in src + for y := 0; y <= size-n; y++ { // offset in dst + copy(dst[y:y+n], src[x:x+n]) + for i := 0; i < y; i++ { + if dst[i] != byte(i&127) { + t.Fatalf("prefix dst[%d] = %d", i, dst[i]) + } + } + for i := y; i < y+n; i++ { + if dst[i] != byte(128+((i-y+x)&127)) { + t.Fatalf("copied dst[%d] = %d", i, dst[i]) + } + dst[i] = byte(i & 127) // reset dst + } + for i := y + n; i < size; i++ { + if dst[i] != byte(i&127) { + t.Fatalf("suffix dst[%d] = %d", i, dst[i]) + } + } + } + } + } +} + +func TestMemmoveAlias(t *testing.T) { + size := 256 + if testing.Short() { + size = 128 + 16 + } + buf := make([]byte, size) + for i := 0; i < size; i++ { + buf[i] = byte(i) + } + for n := 0; n <= size; n++ { + for x := 0; x <= size-n; x++ { // src offset + for y := 0; y <= size-n; y++ { // dst offset + copy(buf[y:y+n], buf[x:x+n]) + for i := 0; i < y; i++ { + if buf[i] != byte(i) { + t.Fatalf("prefix buf[%d] = %d", i, buf[i]) + } + } + for i := y; i < y+n; i++ { + if buf[i] != byte(i-y+x) { + t.Fatalf("copied buf[%d] = %d", i, buf[i]) + } + buf[i] = byte(i) // reset buf + } + for i := y + n; i < size; i++ { + if buf[i] != byte(i) { + t.Fatalf("suffix buf[%d] = %d", i, buf[i]) + } + } + } + } + } +} + +func bmMemmove(n int, b *testing.B) { + x := make([]byte, n) + y := make([]byte, n) + b.SetBytes(int64(n)) + for i := 0; i < b.N; i++ { + copy(x, y) + } +} + +func BenchmarkMemmove0(b *testing.B) { bmMemmove(0, b) } +func BenchmarkMemmove1(b *testing.B) { bmMemmove(1, b) } +func BenchmarkMemmove2(b *testing.B) { bmMemmove(2, b) } +func BenchmarkMemmove3(b *testing.B) { bmMemmove(3, b) } +func BenchmarkMemmove4(b *testing.B) { bmMemmove(4, b) } +func BenchmarkMemmove5(b *testing.B) { bmMemmove(5, b) } +func BenchmarkMemmove6(b *testing.B) { bmMemmove(6, b) } +func BenchmarkMemmove7(b *testing.B) { bmMemmove(7, b) } +func BenchmarkMemmove8(b *testing.B) { bmMemmove(8, b) } +func BenchmarkMemmove9(b *testing.B) { bmMemmove(9, b) } +func BenchmarkMemmove10(b *testing.B) { bmMemmove(10, b) } +func BenchmarkMemmove11(b *testing.B) { bmMemmove(11, b) } +func BenchmarkMemmove12(b *testing.B) { bmMemmove(12, b) } +func BenchmarkMemmove13(b *testing.B) { bmMemmove(13, b) } +func BenchmarkMemmove14(b *testing.B) { bmMemmove(14, b) } +func BenchmarkMemmove15(b *testing.B) { bmMemmove(15, b) } +func BenchmarkMemmove16(b *testing.B) { bmMemmove(16, b) } +func BenchmarkMemmove32(b *testing.B) { bmMemmove(32, b) } +func BenchmarkMemmove64(b *testing.B) { bmMemmove(64, b) } +func BenchmarkMemmove128(b *testing.B) { bmMemmove(128, b) } +func BenchmarkMemmove256(b *testing.B) { bmMemmove(256, b) } +func BenchmarkMemmove512(b *testing.B) { bmMemmove(512, b) } +func BenchmarkMemmove1024(b *testing.B) { bmMemmove(1024, b) } +func BenchmarkMemmove2048(b *testing.B) { bmMemmove(2048, b) } +func BenchmarkMemmove4096(b *testing.B) { bmMemmove(4096, b) } diff --git a/libgo/go/runtime/mfinal_test.go b/libgo/go/runtime/mfinal_test.go index de63271..6efef9b 100644 --- a/libgo/go/runtime/mfinal_test.go +++ b/libgo/go/runtime/mfinal_test.go @@ -9,8 +9,94 @@ import ( "sync" "sync/atomic" "testing" + "time" ) +type Tintptr *int // assignable to *int +type Tint int // *Tint implements Tinter, interface{} + +func (t *Tint) m() {} + +type Tinter interface { + m() +} + +func TestFinalizerType(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skipf("Skipping on non-amd64 machine") + } + + ch := make(chan bool, 10) + finalize := func(x *int) { + if *x != 97531 { + t.Errorf("finalizer %d, want %d", *x, 97531) + } + ch <- true + } + + var finalizerTests = []struct { + convert func(*int) interface{} + finalizer interface{} + }{ + {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }}, + {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }}, + {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }}, + {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }}, + {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }}, + } + + for _, tt := range finalizerTests { + go func() { + v := new(int) + *v = 97531 + runtime.SetFinalizer(tt.convert(v), tt.finalizer) + v = nil + }() + time.Sleep(1 * time.Second) + runtime.GC() + select { + case <-ch: + case <-time.After(time.Second * 4): + t.Errorf("finalizer for type %T didn't run", tt.finalizer) + } + } +} + +type bigValue struct { + fill uint64 + it bool + up string +} + +func TestFinalizerInterfaceBig(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skipf("Skipping on non-amd64 machine") + } + ch := make(chan bool) + go func() { + v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"} + old := *v + runtime.SetFinalizer(v, func(v interface{}) { + i, ok := v.(*bigValue) + if !ok { + t.Errorf("finalizer called with type %T, want *bigValue", v) + } + if *i != old { + t.Errorf("finalizer called with %+v, want %+v", *i, old) + } + close(ch) + }) + v = nil + }() + time.Sleep(1 * time.Second) + runtime.GC() + select { + case <-ch: + case <-time.After(4 * time.Second): + t.Errorf("finalizer for type *bigValue didn't run") + } +} + func fin(v *int) { } diff --git a/libgo/go/runtime/norace_test.go b/libgo/go/runtime/norace_test.go new file mode 100644 index 0000000..a3d5b00 --- /dev/null +++ b/libgo/go/runtime/norace_test.go @@ -0,0 +1,58 @@ +// 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. + +// The file contains tests that can not run under race detector for some reason. +// +build !race + +package runtime_test + +import ( + "runtime" + "sync/atomic" + "testing" +) + +// Syscall tests split stack between Entersyscall and Exitsyscall under race detector. +func BenchmarkSyscall(b *testing.B) { + benchmarkSyscall(b, 0, 1) +} + +func BenchmarkSyscallWork(b *testing.B) { + benchmarkSyscall(b, 100, 1) +} + +func BenchmarkSyscallExcess(b *testing.B) { + benchmarkSyscall(b, 0, 4) +} + +func BenchmarkSyscallExcessWork(b *testing.B) { + benchmarkSyscall(b, 100, 4) +} + +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() + } + } + c <- foo == 42 + }() + } + for p := 0; p < procs; p++ { + <-c + } +} diff --git a/libgo/go/runtime/parfor_test.go b/libgo/go/runtime/parfor_test.go index 4c69a68..de64285 100644 --- a/libgo/go/runtime/parfor_test.go +++ b/libgo/go/runtime/parfor_test.go @@ -102,11 +102,6 @@ func TestParForSetup(t *testing.T) { // Test parallel parallelfor. func TestParForParallel(t *testing.T) { - if GOARCH != "amd64" { - t.Log("temporarily disabled, see http://golang.org/issue/4155") - return - } - N := uint64(1e7) if testing.Short() { N /= 10 diff --git a/libgo/go/runtime/pprof/pprof.go b/libgo/go/runtime/pprof/pprof.go index 32c1098..3b84285 100644 --- a/libgo/go/runtime/pprof/pprof.go +++ b/libgo/go/runtime/pprof/pprof.go @@ -20,8 +20,8 @@ import ( "text/tabwriter" ) -// BUG(rsc): A bug in the OS X Snow Leopard 64-bit kernel prevents -// CPU profiling from giving accurate results on that system. +// BUG(rsc): Profiles are incomplete and inaccuate on NetBSD, OpenBSD, and OS X. +// See http://golang.org/issue/6047 for details. // A Profile is a collection of stack traces showing the call sequences // that led to instances of a particular event, such as allocation. @@ -666,7 +666,7 @@ func writeBlock(w io.Writer, debug int) error { } fmt.Fprint(w, "\n") if debug > 0 { - printStackRecord(w, r.Stack(), false) + printStackRecord(w, r.Stack(), true) } } diff --git a/libgo/go/runtime/pprof/pprof_test.go b/libgo/go/runtime/pprof/pprof_test.go index 5762e17..bdbbf42 100644 --- a/libgo/go/runtime/pprof/pprof_test.go +++ b/libgo/go/runtime/pprof/pprof_test.go @@ -9,57 +9,66 @@ import ( "fmt" "hash/crc32" "os/exec" + "regexp" "runtime" . "runtime/pprof" "strings" + "sync" "testing" + "time" "unsafe" ) func TestCPUProfile(t *testing.T) { - switch runtime.GOOS { - case "darwin": - out, err := exec.Command("uname", "-a").CombinedOutput() - if err != nil { - t.Fatal(err) - } - vers := string(out) - t.Logf("uname -a: %v", vers) - // Lion uses "Darwin Kernel Version 11". - if strings.Contains(vers, "Darwin Kernel Version 10") && strings.Contains(vers, "RELEASE_X86_64") { - t.Skip("skipping test on known-broken kernel (64-bit Leopard / Snow Leopard)") + buf := make([]byte, 100000) + testCPUProfile(t, []string{"crc32.update"}, func() { + // This loop takes about a quarter second on a 2 GHz laptop. + // We only need to get one 100 Hz clock tick, so we've got + // a 25x safety buffer. + for i := 0; i < 1000; i++ { + crc32.ChecksumIEEE(buf) } - case "plan9": - // unimplemented - return - } + }) +} +func TestCPUProfileMultithreaded(t *testing.T) { buf := make([]byte, 100000) - var prof bytes.Buffer - if err := StartCPUProfile(&prof); err != nil { - t.Fatal(err) - } - // This loop takes about a quarter second on a 2 GHz laptop. - // We only need to get one 100 Hz clock tick, so we've got - // a 25x safety buffer. - for i := 0; i < 1000; i++ { - crc32.ChecksumIEEE(buf) - } - StopCPUProfile() + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) + testCPUProfile(t, []string{"crc32.update"}, func() { + c := make(chan int) + go func() { + for i := 0; i < 2000; i++ { + crc32.Update(0, crc32.IEEETable, buf) + } + c <- 1 + }() + // This loop takes about a quarter second on a 2 GHz laptop. + // We only need to get one 100 Hz clock tick, so we've got + // a 25x safety buffer. + for i := 0; i < 2000; i++ { + crc32.ChecksumIEEE(buf) + } + <-c + }) +} +func parseProfile(t *testing.T, bytes []byte, f func(uintptr, []uintptr)) { // Convert []byte to []uintptr. - bytes := prof.Bytes() l := len(bytes) / int(unsafe.Sizeof(uintptr(0))) val := *(*[]uintptr)(unsafe.Pointer(&bytes)) val = val[:l] - if l < 13 { - t.Fatalf("profile too short: %#x", val) + // 5 for the header, 2 for the per-sample header on at least one sample, 3 for the trailer. + if l < 5+2+3 { + t.Logf("profile too short: %#x", val) + if badOS[runtime.GOOS] { + t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS) + return + } + t.FailNow() } - fmt.Println(val, l) hd, val, tl := val[:5], val[5:l-3], val[l-3:] - fmt.Println(hd, val, tl) if hd[0] != 0 || hd[1] != 3 || hd[2] != 0 || hd[3] != 1e6/100 || hd[4] != 0 { t.Fatalf("unexpected header %#x", hd) } @@ -68,26 +77,300 @@ func TestCPUProfile(t *testing.T) { t.Fatalf("malformed end-of-data marker %#x", tl) } - // Check that profile is well formed and contains ChecksumIEEE. - found := false for len(val) > 0 { if len(val) < 2 || val[0] < 1 || val[1] < 1 || uintptr(len(val)) < 2+val[1] { t.Fatalf("malformed profile. leftover: %#x", val) } - for _, pc := range val[2 : 2+val[1]] { + f(val[0], val[2:2+val[1]]) + val = val[2+val[1]:] + } +} + +func testCPUProfile(t *testing.T, need []string, f func()) { + switch runtime.GOOS { + case "darwin": + out, err := exec.Command("uname", "-a").CombinedOutput() + if err != nil { + t.Fatal(err) + } + vers := string(out) + t.Logf("uname -a: %v", vers) + case "plan9": + // unimplemented + return + } + + var prof bytes.Buffer + if err := StartCPUProfile(&prof); err != nil { + t.Fatal(err) + } + f() + StopCPUProfile() + + // Check that profile is well formed and contains ChecksumIEEE. + have := make([]uintptr, len(need)) + parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) { + for _, pc := range stk { f := runtime.FuncForPC(pc) if f == nil { continue } - if strings.Contains(f.Name(), "ChecksumIEEE") || - (strings.Contains(f.Name(), "update") && strings.Contains(f.Name(), "crc32")) { - found = true + for i, name := range need { + if strings.Contains(f.Name(), name) { + have[i] += count + } } } - val = val[2+val[1]:] + }) + + var total uintptr + for i, name := range need { + total += have[i] + t.Logf("%s: %d\n", name, have[i]) + } + ok := true + if total == 0 { + t.Logf("no CPU profile samples collected") + ok = false + } + min := total / uintptr(len(have)) / 3 + for i, name := range need { + if have[i] < min { + t.Logf("%s has %d samples out of %d, want at least %d, ideally %d", name, have[i], total, min, total/uintptr(len(have))) + ok = false + } + } + + if !ok { + if badOS[runtime.GOOS] { + t.Skipf("ignoring failure on %s; see golang.org/issue/6047", runtime.GOOS) + return + } + t.FailNow() + } +} + +func TestCPUProfileWithFork(t *testing.T) { + // Fork can hang if preempted with signals frequently enough (see issue 5517). + // Ensure that we do not do this. + heap := 1 << 30 + if testing.Short() { + heap = 100 << 20 } + // This makes fork slower. + garbage := make([]byte, heap) + // Need to touch the slice, otherwise it won't be paged in. + done := make(chan bool) + go func() { + for i := range garbage { + garbage[i] = 42 + } + done <- true + }() + <-done - if !found { - t.Fatal("did not find ChecksumIEEE in the profile") + var prof bytes.Buffer + if err := StartCPUProfile(&prof); err != nil { + t.Fatal(err) } + defer StopCPUProfile() + + for i := 0; i < 10; i++ { + exec.Command("go").CombinedOutput() + } +} + +// Test that profiler does not observe runtime.gogo as "user" goroutine execution. +// If it did, it would see inconsistent state and would either record an incorrect stack +// or crash because the stack was malformed. +func TestGoroutineSwitch(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("flaky test; see http://golang.org/issue/6417") + } + // How much to try. These defaults take about 1 seconds + // on a 2012 MacBook Pro. The ones in short mode take + // about 0.1 seconds. + tries := 10 + count := 1000000 + if testing.Short() { + tries = 1 + } + for try := 0; try < tries; try++ { + var prof bytes.Buffer + if err := StartCPUProfile(&prof); err != nil { + t.Fatal(err) + } + for i := 0; i < count; i++ { + runtime.Gosched() + } + StopCPUProfile() + + // Read profile to look for entries for runtime.gogo with an attempt at a traceback. + // The special entry + parseProfile(t, prof.Bytes(), func(count uintptr, stk []uintptr) { + // An entry with two frames with 'System' in its top frame + // exists to record a PC without a traceback. Those are okay. + if len(stk) == 2 { + f := runtime.FuncForPC(stk[1]) + if f != nil && f.Name() == "System" { + return + } + } + + // Otherwise, should not see runtime.gogo. + // The place we'd see it would be the inner most frame. + f := runtime.FuncForPC(stk[0]) + if f != nil && f.Name() == "runtime.gogo" { + var buf bytes.Buffer + for _, pc := range stk { + f := runtime.FuncForPC(pc) + if f == nil { + fmt.Fprintf(&buf, "%#x ?:0\n", pc) + } else { + file, line := f.FileLine(pc) + fmt.Fprintf(&buf, "%#x %s:%d\n", pc, file, line) + } + } + t.Fatalf("found profile entry for runtime.gogo:\n%s", buf.String()) + } + }) + } +} + +// Operating systems that are expected to fail the tests. See issue 6047. +var badOS = map[string]bool{ + "darwin": true, + "netbsd": true, + "openbsd": true, +} + +func TestBlockProfile(t *testing.T) { + t.Skip("lots of details are different for gccgo; FIXME") + type TestCase struct { + name string + f func() + re string + } + tests := [...]TestCase{ + {"chan recv", blockChanRecv, ` +[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ +# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanRecv\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +`}, + {"chan send", blockChanSend, ` +[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ +# 0x[0-9,a-f]+ runtime\.chansend1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanSend\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +`}, + {"chan close", blockChanClose, ` +[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ +# 0x[0-9,a-f]+ runtime\.chanrecv1\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.blockChanClose\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +`}, + {"select recv async", blockSelectRecvAsync, ` +[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ +# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectRecvAsync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +`}, + {"select send sync", blockSelectSendSync, ` +[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ +# 0x[0-9,a-f]+ runtime\.selectgo\+0x[0-9,a-f]+ .*/src/pkg/runtime/chan.c:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.blockSelectSendSync\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +`}, + {"mutex", blockMutex, ` +[0-9]+ [0-9]+ @ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ 0x[0-9,a-f]+ +# 0x[0-9,a-f]+ sync\.\(\*Mutex\)\.Lock\+0x[0-9,a-f]+ .*/src/pkg/sync/mutex\.go:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.blockMutex\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +# 0x[0-9,a-f]+ runtime/pprof_test\.TestBlockProfile\+0x[0-9,a-f]+ .*/src/pkg/runtime/pprof/pprof_test.go:[0-9]+ +`}, + } + + runtime.SetBlockProfileRate(1) + defer runtime.SetBlockProfileRate(0) + for _, test := range tests { + test.f() + } + var w bytes.Buffer + Lookup("block").WriteTo(&w, 1) + prof := w.String() + + if !strings.HasPrefix(prof, "--- contention:\ncycles/second=") { + t.Fatalf("Bad profile header:\n%v", prof) + } + + for _, test := range tests { + if !regexp.MustCompile(test.re).MatchString(prof) { + t.Fatalf("Bad %v entry, expect:\n%v\ngot:\n%v", test.name, test.re, prof) + } + } +} + +const blockDelay = 10 * time.Millisecond + +func blockChanRecv() { + c := make(chan bool) + go func() { + time.Sleep(blockDelay) + c <- true + }() + <-c +} + +func blockChanSend() { + c := make(chan bool) + go func() { + time.Sleep(blockDelay) + <-c + }() + c <- true +} + +func blockChanClose() { + c := make(chan bool) + go func() { + time.Sleep(blockDelay) + close(c) + }() + <-c +} + +func blockSelectRecvAsync() { + c := make(chan bool, 1) + c2 := make(chan bool, 1) + go func() { + time.Sleep(blockDelay) + c <- true + }() + select { + case <-c: + case <-c2: + } +} + +func blockSelectSendSync() { + c := make(chan bool) + c2 := make(chan bool) + go func() { + time.Sleep(blockDelay) + <-c + }() + select { + case c <- true: + case c2 <- true: + } +} + +func blockMutex() { + var mu sync.Mutex + mu.Lock() + go func() { + time.Sleep(blockDelay) + mu.Unlock() + }() + mu.Lock() } diff --git a/libgo/go/runtime/proc_test.go b/libgo/go/runtime/proc_test.go index 21fb9c2..29f71e7 100644 --- a/libgo/go/runtime/proc_test.go +++ b/libgo/go/runtime/proc_test.go @@ -8,6 +8,7 @@ import ( "math" "runtime" "sync/atomic" + "syscall" "testing" "time" ) @@ -92,6 +93,35 @@ func TestYieldLocked(t *testing.T) { <-c } +func TestGoroutineParallelism(t *testing.T) { + P := 4 + N := 10 + if testing.Short() { + P = 3 + N = 3 + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) + for try := 0; try < N; try++ { + done := make(chan bool) + x := uint32(0) + for p := 0; p < P; p++ { + // Test that all P goroutines are scheduled at the same time + go func(p int) { + for i := 0; i < 3; i++ { + expected := uint32(P*i + p) + for atomic.LoadUint32(&x) != expected { + } + atomic.StoreUint32(&x, expected+1) + } + done <- true + }(p) + } + for p := 0; p < P; p++ { + <-done + } + } +} + func TestBlockLocked(t *testing.T) { const N = 10 c := make(chan bool) @@ -107,86 +137,212 @@ func TestBlockLocked(t *testing.T) { } } -func stackGrowthRecursive(i int) { - var pad [128]uint64 - if i != 0 && pad[0] == 0 { - stackGrowthRecursive(i - 1) +func TestTimerFairness(t *testing.T) { + done := make(chan bool) + c := make(chan bool) + for i := 0; i < 2; i++ { + go func() { + for { + select { + case c <- true: + case <-done: + return + } + } + }() + } + + timer := time.After(20 * time.Millisecond) + for { + select { + case <-c: + case <-timer: + close(done) + return + } } } -func TestSchedLocalQueue(t *testing.T) { - runtime.TestSchedLocalQueue1() +func TestTimerFairness2(t *testing.T) { + done := make(chan bool) + c := make(chan bool) + for i := 0; i < 2; i++ { + go func() { + timer := time.After(20 * time.Millisecond) + var buf [1]byte + for { + syscall.Read(0, buf[0:0]) + select { + case c <- true: + case <-c: + case <-timer: + done <- true + return + } + } + }() + } + <-done + <-done } -func TestSchedLocalQueueSteal(t *testing.T) { - runtime.TestSchedLocalQueueSteal1() +// The function is used to test preemption at split stack checks. +// Declaring a var avoids inlining at the call site. +var preempt = func() int { + var a [128]int + sum := 0 + for _, v := range a { + sum += v + } + return sum } -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) +func TestPreemption(t *testing.T) { + t.Skip("gccgo does not implement preemption") + // Test that goroutines are preempted at function calls. + N := 5 + if testing.Short() { + N = 2 + } + c := make(chan bool) + var x uint32 + for g := 0; g < 2; g++ { + go func(g int) { + for i := 0; i < N; i++ { + for atomic.LoadUint32(&x) != uint32(g) { + preempt() } + atomic.StoreUint32(&x, uint32(1-g)) } c <- true + }(g) + } + <-c + <-c +} + +func TestPreemptionGC(t *testing.T) { + t.Skip("gccgo does not implement preemption") + // Test that pending GC preempts running goroutines. + P := 5 + N := 10 + if testing.Short() { + P = 3 + N = 2 + } + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1)) + var stop uint32 + for i := 0; i < P; i++ { + go func() { + for atomic.LoadUint32(&stop) == 0 { + preempt() + } }() } - for p := 0; p < procs; p++ { - <-c + for i := 0; i < N; i++ { + runtime.Gosched() + runtime.GC() } + atomic.StoreUint32(&stop, 1) } -func BenchmarkStackGrowth(b *testing.B) { - benchmarkStackGrowth(b, 10) +func stackGrowthRecursive(i int) { + var pad [128]uint64 + if i != 0 && pad[0] == 0 { + stackGrowthRecursive(i - 1) + } } -func BenchmarkStackGrowthDeep(b *testing.B) { - benchmarkStackGrowth(b, 1024) +func TestPreemptSplitBig(t *testing.T) { + if testing.Short() { + t.Skip("skipping in -short mode") + } + t.Skip("gccgo does not implement preemption") + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) + stop := make(chan int) + go big(stop) + for i := 0; i < 3; i++ { + time.Sleep(10 * time.Microsecond) // let big start running + runtime.GC() + } + close(stop) +} + +func big(stop chan int) int { + n := 0 + for { + // delay so that gc is sure to have asked for a preemption + for i := 0; i < 1e9; i++ { + n++ + } + + // call bigframe, which used to miss the preemption in its prologue. + bigframe(stop) + + // check if we've been asked to stop. + select { + case <-stop: + return n + } + } +} + +func bigframe(stop chan int) int { + // not splitting the stack will overflow. + // small will notice that it needs a stack split and will + // catch the overflow. + var x [8192]byte + return small(stop, &x) } -func BenchmarkSyscall(b *testing.B) { - benchmarkSyscall(b, 0, 1) +func small(stop chan int, x *[8192]byte) int { + for i := range x { + x[i] = byte(i) + } + sum := 0 + for i := range x { + sum += int(x[i]) + } + + // keep small from being a leaf function, which might + // make it not do any stack check at all. + nonleaf(stop) + + return sum } -func BenchmarkSyscallWork(b *testing.B) { - benchmarkSyscall(b, 100, 1) +func nonleaf(stop chan int) bool { + // do something that won't be inlined: + select { + case <-stop: + return true + default: + return false + } } -func BenchmarkSyscallExcess(b *testing.B) { - benchmarkSyscall(b, 0, 4) +func TestSchedLocalQueue(t *testing.T) { + runtime.TestSchedLocalQueue1() } -func BenchmarkSyscallExcessWork(b *testing.B) { - benchmarkSyscall(b, 100, 4) +func TestSchedLocalQueueSteal(t *testing.T) { + runtime.TestSchedLocalQueueSteal1() } -func benchmarkSyscall(b *testing.B, work, excess int) { +func benchmarkStackGrowth(b *testing.B, rec int) { const CallsPerSched = 1000 - procs := runtime.GOMAXPROCS(-1) * excess + procs := runtime.GOMAXPROCS(-1) 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() + stackGrowthRecursive(rec) } } - c <- foo == 42 + c <- true }() } for p := 0; p < procs; p++ { @@ -194,6 +350,14 @@ func benchmarkSyscall(b *testing.B, work, excess int) { } } +func BenchmarkStackGrowth(b *testing.B) { + benchmarkStackGrowth(b, 10) +} + +func BenchmarkStackGrowthDeep(b *testing.B) { + benchmarkStackGrowth(b, 1024) +} + func BenchmarkCreateGoroutines(b *testing.B) { benchmarkCreateGoroutines(b, 1) } diff --git a/libgo/go/runtime/runtime_test.go b/libgo/go/runtime/runtime_test.go index e458793..d121929 100644 --- a/libgo/go/runtime/runtime_test.go +++ b/libgo/go/runtime/runtime_test.go @@ -6,6 +6,12 @@ package runtime_test import ( "io" + // "io/ioutil" + // "os" + // "os/exec" + // . "runtime" + // "strconv" + // "strings" "testing" ) @@ -79,3 +85,43 @@ func BenchmarkDeferMany(b *testing.B) { }(1, 2, 3) } } + +/* The go tool is not present in gccgo. + +// The profiling signal handler needs to know whether it is executing runtime.gogo. +// The constant RuntimeGogoBytes in arch_*.h gives the size of the function; +// we don't have a way to obtain it from the linker (perhaps someday). +// Test that the constant matches the size determined by 'go tool nm -S'. +// The value reported will include the padding between runtime.gogo and the +// next function in memory. That's fine. +func TestRuntimeGogoBytes(t *testing.T) { + dir, err := ioutil.TempDir("", "go-build") + if err != nil { + t.Fatalf("failed to create temp directory: %v", err) + } + defer os.RemoveAll(dir) + + out, err := exec.Command("go", "build", "-o", dir+"/hello", "../../../test/helloworld.go").CombinedOutput() + if err != nil { + t.Fatalf("building hello world: %v\n%s", err, out) + } + + out, err = exec.Command("go", "tool", "nm", "-S", dir+"/hello").CombinedOutput() + if err != nil { + t.Fatalf("go tool nm: %v\n%s", err, out) + } + + for _, line := range strings.Split(string(out), "\n") { + f := strings.Fields(line) + if len(f) == 4 && f[3] == "runtime.gogo" { + size, _ := strconv.Atoi(f[1]) + if GogoBytes() != int32(size) { + t.Fatalf("RuntimeGogoBytes = %d, should be %d", GogoBytes(), size) + } + return + } + } + + t.Fatalf("go tool nm did not report size for runtime.gogo") +} +*/ diff --git a/libgo/go/sort/example_interface_test.go b/libgo/go/sort/example_interface_test.go index 4c88821..442204e 100644 --- a/libgo/go/sort/example_interface_test.go +++ b/libgo/go/sort/example_interface_test.go @@ -9,69 +9,36 @@ import ( "sort" ) -type Grams int - -func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) } - -type Organ struct { - Name string - Weight Grams +type Person struct { + Name string + Age int } -type Organs []*Organ - -func (s Organs) Len() int { return len(s) } -func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// ByName implements sort.Interface by providing Less and using the Len and -// Swap methods of the embedded Organs value. -type ByName struct{ Organs } - -func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name } +func (p Person) String() string { + return fmt.Sprintf("%s: %d", p.Name, p.Age) +} -// ByWeight implements sort.Interface by providing Less and using the Len and -// Swap methods of the embedded Organs value. -type ByWeight struct{ Organs } +// ByAge implements sort.Interface for []Person based on +// the Age field. +type ByAge []Person -func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight } +func (a ByAge) Len() int { return len(a) } +func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age } -func ExampleInterface() { - s := []*Organ{ - {"brain", 1340}, - {"heart", 290}, - {"liver", 1494}, - {"pancreas", 131}, - {"prostate", 62}, - {"spleen", 162}, +func Example() { + people := []Person{ + {"Bob", 31}, + {"John", 42}, + {"Michael", 17}, + {"Jenny", 26}, } - sort.Sort(ByWeight{s}) - fmt.Println("Organs by weight:") - printOrgans(s) - - sort.Sort(ByName{s}) - fmt.Println("Organs by name:") - printOrgans(s) + fmt.Println(people) + sort.Sort(ByAge(people)) + fmt.Println(people) // Output: - // Organs by weight: - // prostate (62g) - // pancreas (131g) - // spleen (162g) - // heart (290g) - // brain (1340g) - // liver (1494g) - // Organs by name: - // brain (1340g) - // heart (290g) - // liver (1494g) - // pancreas (131g) - // prostate (62g) - // spleen (162g) -} - -func printOrgans(s []*Organ) { - for _, o := range s { - fmt.Printf("%-8s (%v)\n", o.Name, o.Weight) - } + // [Bob: 31 John: 42 Michael: 17 Jenny: 26] + // [Michael: 17 Jenny: 26 Bob: 31 John: 42] } diff --git a/libgo/go/sort/example_multi_test.go b/libgo/go/sort/example_multi_test.go index d0a9e7d..ac31654 100644 --- a/libgo/go/sort/example_multi_test.go +++ b/libgo/go/sort/example_multi_test.go @@ -26,6 +26,7 @@ type multiSorter struct { // Sort sorts the argument slice according to the less functions passed to OrderedBy. func (ms *multiSorter) Sort(changes []Change) { + ms.changes = changes sort.Sort(ms) } @@ -33,8 +34,7 @@ func (ms *multiSorter) Sort(changes []Change) { // Call its Sort method to sort the data. func OrderedBy(less ...lessFunc) *multiSorter { return &multiSorter{ - changes: changes, - less: less, + less: less, } } @@ -108,11 +108,10 @@ func Example_sortMultiKeys() { OrderedBy(user).Sort(changes) fmt.Println("By user:", changes) - // multiSorter implements the Sort interface, so we can also do this. - sort.Sort(OrderedBy(user, increasingLines)) + // More examples. + OrderedBy(user, increasingLines).Sort(changes) fmt.Println("By user,<lines:", changes) - // More examples. OrderedBy(user, decreasingLines).Sort(changes) fmt.Println("By user,>lines:", changes) @@ -123,10 +122,10 @@ func Example_sortMultiKeys() { fmt.Println("By language,<lines,user:", changes) // Output: - //By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}] - //By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}] - //By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}] - //By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}] - //By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}] + // By user: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken Go 200} {ken C 150} {r Go 100} {r C 150} {rsc Go 200}] + // By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}] + // By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}] + // By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}] + // By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}] } diff --git a/libgo/go/sort/example_wrapper_test.go b/libgo/go/sort/example_wrapper_test.go new file mode 100644 index 0000000..cf6d74c --- /dev/null +++ b/libgo/go/sort/example_wrapper_test.go @@ -0,0 +1,77 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package sort_test + +import ( + "fmt" + "sort" +) + +type Grams int + +func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) } + +type Organ struct { + Name string + Weight Grams +} + +type Organs []*Organ + +func (s Organs) Len() int { return len(s) } +func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +// ByName implements sort.Interface by providing Less and using the Len and +// Swap methods of the embedded Organs value. +type ByName struct{ Organs } + +func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name } + +// ByWeight implements sort.Interface by providing Less and using the Len and +// Swap methods of the embedded Organs value. +type ByWeight struct{ Organs } + +func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight } + +func Example_sortWrapper() { + s := []*Organ{ + {"brain", 1340}, + {"heart", 290}, + {"liver", 1494}, + {"pancreas", 131}, + {"prostate", 62}, + {"spleen", 162}, + } + + sort.Sort(ByWeight{s}) + fmt.Println("Organs by weight:") + printOrgans(s) + + sort.Sort(ByName{s}) + fmt.Println("Organs by name:") + printOrgans(s) + + // Output: + // Organs by weight: + // prostate (62g) + // pancreas (131g) + // spleen (162g) + // heart (290g) + // brain (1340g) + // liver (1494g) + // Organs by name: + // brain (1340g) + // heart (290g) + // liver (1494g) + // pancreas (131g) + // prostate (62g) + // spleen (162g) +} + +func printOrgans(s []*Organ) { + for _, o := range s { + fmt.Printf("%-8s (%v)\n", o.Name, o.Weight) + } +} diff --git a/libgo/go/sort/search_test.go b/libgo/go/sort/search_test.go index 80b9abd..4265b95 100644 --- a/libgo/go/sort/search_test.go +++ b/libgo/go/sort/search_test.go @@ -128,6 +128,9 @@ func runSearchWrappers() { } func TestSearchWrappersDontAlloc(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } diff --git a/libgo/go/sort/sort.go b/libgo/go/sort/sort.go index d3092e8..f06eb38 100644 --- a/libgo/go/sort/sort.go +++ b/libgo/go/sort/sort.go @@ -12,8 +12,8 @@ package sort type Interface interface { // Len is the number of elements in the collection. Len() int - // Less returns whether the element with index i should sort - // before the element with index j. + // Less reports whether the element with + // index i should sort before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) @@ -283,3 +283,192 @@ func Float64sAreSorted(a []float64) bool { return IsSorted(Float64Slice(a)) } // StringsAreSorted tests whether a slice of strings is sorted in increasing order. 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. +// +// Remarks on other algoritms evaluated: +// - GCC's 4.6.3 stable_sort with merge_without_buffer from libstdc++: +// Not faster. +// - GCC's __rotate for block rotations: Not faster. +// - "Practical in-place mergesort" from Jyrki Katajainen, Tomi A. Pasanen +// and Jukka Teuhola; Nordic Journal of Computing 3,1 (1996), 27-40: +// The given algorithms are in-place, number of Swap and Assignments +// grow as n log n but the algorithm is not stable. +// - "Fast Stable In-Plcae Sorting with O(n) Data Moves" J.I. Munro and +// V. Raman in Algorithmica (1996) 16, 115-160: +// This algorithm either needs additional 2n bits or works only if there +// are enough different elements available to encode some permutations +// which have to be undone later (so not stable an any input). +// - All the optimal in-place sorting/merging algorithms I found are either +// unstable or rely on enough different elements in each step to encode the +// performed block rearrangements. See also "In-Place Merging Algorithms", +// Denham Coates-Evely, Department of Computer Science, Kings College, +// January 2004 and the reverences in there. +// - Often "optimal" algorithms are optimal in the number of assignments +// but Interface has only Swap as operation. + +// Stable sorts data while keeping the original order of equal elements. +// +// It makes one call to data.Len to determine n, O(n*log(n)) calls to +// data.Less and O(n*log(n)*log(n)) calls to data.Swap. +func Stable(data Interface) { + n := data.Len() + blockSize := 20 + a, b := 0, blockSize + for b <= n { + insertionSort(data, a, b) + a = b + b += blockSize + } + insertionSort(data, a, n) + + for blockSize < n { + a, b = 0, 2*blockSize + for b <= n { + symMerge(data, a, a+blockSize, b) + a = b + b += 2 * blockSize + } + symMerge(data, a, a+blockSize, n) + blockSize *= 2 + } +} + +// SymMerge merges the two sorted subsequences data[a:m] and data[m:b] using +// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum +// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz +// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in +// Computer Science, pages 714-723. Springer, 2004. +// +// Let M = m-a and N = b-n. Wolog M < N. +// The recursion depth is bound by ceil(log(N+M)). +// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. +// The algorithm needs O((M+N)*log(M)) calls to data.Swap. +// +// The paper gives O((M+N)*log(M)) as the number of assignments assuming a +// rotation algorithm wich uses O(M+N+gcd(M+N)) assignments. The argumentation +// in the paper carries through for Swap operations, especially as the block +// swapping rotate uses only O(M+N) Swaps. +func symMerge(data Interface, a, m, b int) { + if a >= m || m >= b { + return + } + + mid := a + (b-a)/2 + n := mid + m + start := 0 + if m > mid { + start = n - b + r, p := mid, n-1 + for start < r { + c := start + (r-start)/2 + if !data.Less(p-c, c) { + start = c + 1 + } else { + r = c + } + } + } else { + start = a + r, p := m, n-1 + for start < r { + c := start + (r-start)/2 + if !data.Less(p-c, c) { + start = c + 1 + } else { + r = c + } + } + } + end := n - start + rotate(data, start, m, end) + symMerge(data, a, start, mid) + symMerge(data, mid, end, b) +} + +// Rotate two consecutives blocks u = data[a:m] and v = data[m:b] in data: +// Data of the form 'x u v y' is changed to 'x v u y'. +// Rotate performs at most b-a many calls to data.Swap. +func rotate(data Interface, a, m, b int) { + i := m - a + if i == 0 { + return + } + j := b - m + if j == 0 { + return + } + + if i == j { + swapRange(data, a, m, i) + return + } + + p := a + i + for i != j { + if i > j { + swapRange(data, p-i, p, j) + i -= j + } else { + swapRange(data, p-i, p+j-i, i) + j -= i + } + } + swapRange(data, p-i, p, i) +} + +/* +Complexity of Stable Sorting + + +Complexity of block swapping rotation + +Each Swap puts one new element into its correct, final position. +Elements which reach their final position are no longer moved. +Thus block swapping rotation needs |u|+|v| calls to Swaps. +This is best possible as each element might need a move. + +Pay attention when comparing to other optimal algorithms which +typically count the number of assignments instead of swaps: +E.g. the optimal algorithm of Dudzinski and Dydek for in-place +rotations uses O(u + v + gcd(u,v)) assignments which is +better than our O(3 * (u+v)) as gcd(u,v) <= u. + + +Stable sorting by SymMerge and BlockSwap rotations + +SymMerg complexity for same size input M = N: +Calls to Less: O(M*log(N/M+1)) = O(N*log(2)) = O(N) +Calls to Swap: O((M+N)*log(M)) = O(2*N*log(N)) = O(N*log(N)) + +(The following argument does not fuzz over a missing -1 or +other stuff which does not impact the final result). + +Let n = data.Len(). Assume n = 2^k. + +Plain merge sort performs log(n) = k iterations. +On iteration i the algorithm merges 2^(k-i) blocks, each of size 2^i. + +Thus iteration i of merge sort performs: +Calls to Less O(2^(k-i) * 2^i) = O(2^k) = O(2^log(n)) = O(n) +Calls to Swap O(2^(k-i) * 2^i * log(2^i)) = O(2^k * i) = O(n*i) + +In total k = log(n) iterations are performed; so in total: +Calls to Less O(log(n) * n) +Calls to Swap O(n + 2*n + 3*n + ... + (k-1)*n + k*n) + = O((k/2) * k * n) = O(n * k^2) = O(n * log^2(n)) + + +Above results should generalize to arbitrary n = 2^k + p +and should not be influenced by the initial insertion sort phase: +Insertion sort is O(n^2) on Swap and Less, thus O(bs^2) per block of +size bs at n/bs blocks: O(bs*n) Swaps and Less during insertion sort. +Merge sort iterations start at i = log(bs). With t = log(bs) constant: +Calls to Less O((log(n)-t) * n + bs*n) = O(log(n)*n + (bs-t)*n) + = O(n * log(n)) +Calls to Swap O(n * log^2(n) - (t^2+t)/2*n) = O(n * log^2(n)) + +*/ diff --git a/libgo/go/sort/sort_test.go b/libgo/go/sort/sort_test.go index 5daf848..6c36f30 100644 --- a/libgo/go/sort/sort_test.go +++ b/libgo/go/sort/sort_test.go @@ -122,6 +122,19 @@ func BenchmarkSortString1K(b *testing.B) { } } +func BenchmarkStableString1K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]string, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = strconv.Itoa(i ^ 0x2cc) + } + b.StartTimer() + Stable(StringSlice(data)) + b.StopTimer() + } +} + func BenchmarkSortInt1K(b *testing.B) { b.StopTimer() for i := 0; i < b.N; i++ { @@ -135,6 +148,19 @@ func BenchmarkSortInt1K(b *testing.B) { } } +func BenchmarkStableInt1K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<10) + for i := 0; i < len(data); i++ { + data[i] = i ^ 0x2cc + } + b.StartTimer() + Stable(IntSlice(data)) + b.StopTimer() + } +} + func BenchmarkSortInt64K(b *testing.B) { b.StopTimer() for i := 0; i < b.N; i++ { @@ -148,6 +174,19 @@ func BenchmarkSortInt64K(b *testing.B) { } } +func BenchmarkStableInt64K(b *testing.B) { + b.StopTimer() + for i := 0; i < b.N; i++ { + data := make([]int, 1<<16) + for i := 0; i < len(data); i++ { + data[i] = i ^ 0xcccc + } + b.StartTimer() + Stable(IntSlice(data)) + b.StopTimer() + } +} + const ( _Sawtooth = iota _Rand @@ -204,7 +243,7 @@ func lg(n int) int { return i } -func testBentleyMcIlroy(t *testing.T, sort func(Interface)) { +func testBentleyMcIlroy(t *testing.T, sort func(Interface), maxswap func(int) int) { sizes := []int{100, 1023, 1024, 1025} if testing.Short() { sizes = []int{100, 127, 128, 129} @@ -278,7 +317,7 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) { } desc := fmt.Sprintf("n=%d m=%d dist=%s mode=%s", n, m, dists[dist], modes[mode]) - d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: n * lg(n) * 12 / 10} + d := &testingData{desc: desc, t: t, data: mdata[0:n], maxswap: maxswap(n)} sort(d) // Uncomment if you are trying to improve the number of compares/swaps. //t.Logf("%s: ncmp=%d, nswp=%d", desc, d.ncmp, d.nswap) @@ -303,11 +342,15 @@ func testBentleyMcIlroy(t *testing.T, sort func(Interface)) { } func TestSortBM(t *testing.T) { - testBentleyMcIlroy(t, Sort) + testBentleyMcIlroy(t, Sort, func(n int) int { return n * lg(n) * 12 / 10 }) } func TestHeapsortBM(t *testing.T) { - testBentleyMcIlroy(t, Heapsort) + testBentleyMcIlroy(t, Heapsort, func(n int) int { return n * lg(n) * 12 / 10 }) +} + +func TestStableBM(t *testing.T) { + testBentleyMcIlroy(t, Stable, func(n int) int { return n * lg(n) * lg(n) / 3 }) } // This is based on the "antiquicksort" implementation by M. Douglas McIlroy. @@ -357,3 +400,154 @@ func TestAdversary(t *testing.T) { d := &adversaryTestingData{data, make(map[int]int), 0} Sort(d) // This should degenerate to heapsort. } + +func TestStableInts(t *testing.T) { + data := ints + Stable(IntSlice(data[0:])) + if !IntsAreSorted(data[0:]) { + t.Errorf("nsorted %v\n got %v", ints, data) + } +} + +type intPairs []struct { + a, b int +} + +// IntPairs compare on a only. +func (d intPairs) Len() int { return len(d) } +func (d intPairs) Less(i, j int) bool { return d[i].a < d[j].a } +func (d intPairs) Swap(i, j int) { d[i], d[j] = d[j], d[i] } + +// Record initial order in B. +func (d intPairs) initB() { + for i := range d { + d[i].b = i + } +} + +// InOrder checks if a-equal elements were not reordered. +func (d intPairs) inOrder() bool { + lastA, lastB := -1, 0 + for i := 0; i < len(d); i++ { + if lastA != d[i].a { + lastA = d[i].a + lastB = d[i].b + continue + } + if d[i].b <= lastB { + return false + } + lastB = d[i].b + } + return true +} + +func TestStability(t *testing.T) { + n, m := 100000, 1000 + if testing.Short() { + n, m = 1000, 100 + } + data := make(intPairs, n) + + // random distribution + for i := 0; i < len(data); i++ { + data[i].a = rand.Intn(m) + } + if IsSorted(data) { + t.Fatalf("terrible rand.rand") + } + data.initB() + Stable(data) + if !IsSorted(data) { + t.Errorf("Stable didn't sort %d ints", n) + } + if !data.inOrder() { + t.Errorf("Stable wasn't stable on %d ints", n) + } + + // already sorted + data.initB() + Stable(data) + if !IsSorted(data) { + t.Errorf("Stable shuffeled sorted %d ints (order)", n) + } + if !data.inOrder() { + t.Errorf("Stable shuffeled sorted %d ints (stability)", n) + } + + // sorted reversed + for i := 0; i < len(data); i++ { + data[i].a = len(data) - i + } + data.initB() + Stable(data) + if !IsSorted(data) { + t.Errorf("Stable didn't sort %d ints", n) + } + if !data.inOrder() { + t.Errorf("Stable wasn't stable on %d ints", n) + } +} + +var countOpsSizes = []int{1e2, 3e2, 1e3, 3e3, 1e4, 3e4, 1e5, 3e5, 1e6} + +func countOps(t *testing.T, algo func(Interface), name string) { + sizes := countOpsSizes + if testing.Short() { + sizes = sizes[:5] + } + if !testing.Verbose() { + t.Skip("Counting skipped as non-verbose mode.") + } + for _, n := range sizes { + td := testingData{ + desc: name, + t: t, + data: make([]int, n), + maxswap: 1<<31 - 1, + } + for i := 0; i < n; i++ { + td.data[i] = rand.Intn(n / 5) + } + algo(&td) + t.Logf("%s %8d elements: %11d Swap, %10d Less", name, n, td.nswap, td.ncmp) + } +} + +func TestCountStableOps(t *testing.T) { countOps(t, Stable, "Stable") } +func TestCountSortOps(t *testing.T) { countOps(t, Sort, "Sort ") } + +func bench(b *testing.B, size int, algo func(Interface), name string) { + b.StopTimer() + data := make(intPairs, size) + x := ^uint32(0) + for i := 0; i < b.N; i++ { + for n := size - 3; n <= size+3; n++ { + for i := 0; i < len(data); i++ { + x += x + x ^= 1 + if int32(x) < 0 { + x ^= 0x88888eef + } + data[i].a = int(x % uint32(n/5)) + } + data.initB() + b.StartTimer() + algo(data) + b.StopTimer() + if !IsSorted(data) { + b.Errorf("%s did not sort %d ints", name, n) + } + if name == "Stable" && !data.inOrder() { + b.Errorf("%s unstable on %d ints", name, n) + } + } + } +} + +func BenchmarkSort1e2(b *testing.B) { bench(b, 1e2, Sort, "Sort") } +func BenchmarkStable1e2(b *testing.B) { bench(b, 1e2, Stable, "Stable") } +func BenchmarkSort1e4(b *testing.B) { bench(b, 1e4, Sort, "Sort") } +func BenchmarkStable1e4(b *testing.B) { bench(b, 1e4, Stable, "Stable") } +func BenchmarkSort1e6(b *testing.B) { bench(b, 1e6, Sort, "Sort") } +func BenchmarkStable1e6(b *testing.B) { bench(b, 1e6, Stable, "Stable") } diff --git a/libgo/go/strconv/atof.go b/libgo/go/strconv/atof.go index c9e243a..1dc521f 100644 --- a/libgo/go/strconv/atof.go +++ b/libgo/go/strconv/atof.go @@ -542,11 +542,11 @@ func atof64(s string) (f float64, err error) { // The errors that ParseFloat returns have concrete type *NumError // and include err.Num = s. // -// If s is not syntactically well-formed, ParseFloat returns err.Error = ErrSyntax. +// If s is not syntactically well-formed, ParseFloat returns err.Err = ErrSyntax. // // If s is syntactically well-formed but is more than 1/2 ULP // away from the largest floating point number of the given size, -// ParseFloat returns f = ±Inf, err.Error = ErrRange. +// ParseFloat returns f = ±Inf, err.Err = ErrRange. func ParseFloat(s string, bitSize int) (f float64, err error) { if bitSize == 32 { f1, err1 := atof32(s) diff --git a/libgo/go/strconv/atoi.go b/libgo/go/strconv/atoi.go index 21c6900..2d0db71 100644 --- a/libgo/go/strconv/atoi.go +++ b/libgo/go/strconv/atoi.go @@ -33,7 +33,8 @@ func rangeError(fn, str string) *NumError { const intSize = 32 << uint(^uint(0)>>63) -const IntSize = intSize // number of bits in int, uint (32 or 64) +// IntSize is the size in bits of an int or uint value. +const IntSize = intSize // Return the first number n such that n*base >= 1<<64. func cutoff64(base int) uint64 { @@ -141,9 +142,9 @@ Error: // // The errors that ParseInt returns have concrete type *NumError // and include err.Num = s. If s is empty or contains invalid -// digits, err.Error = ErrSyntax; if the value corresponding +// digits, err.Err = ErrSyntax; if the value corresponding // to s cannot be represented by a signed integer of the -// given size, err.Error = ErrRange. +// given size, err.Err = ErrRange. func ParseInt(s string, base int, bitSize int) (i int64, err error) { const fnParseInt = "ParseInt" diff --git a/libgo/go/strconv/quote.go b/libgo/go/strconv/quote.go index 8cbef88..7d6cdcf 100644 --- a/libgo/go/strconv/quote.go +++ b/libgo/go/strconv/quote.go @@ -133,7 +133,7 @@ func QuoteRuneToASCII(r rune) string { return quoteWith(string(r), '\'', true) } -// AppendQuoteRune appends a single-quoted Go character literal representing the rune, +// AppendQuoteRuneToASCII appends a single-quoted Go character literal representing the rune, // as generated by QuoteRuneToASCII, to dst and returns the extended buffer. func AppendQuoteRuneToASCII(dst []byte, r rune) []byte { return append(dst, QuoteRuneToASCII(r)...) diff --git a/libgo/go/strconv/strconv_test.go b/libgo/go/strconv/strconv_test.go index 381874b..207e00e 100644 --- a/libgo/go/strconv/strconv_test.go +++ b/libgo/go/strconv/strconv_test.go @@ -46,6 +46,9 @@ var ( ) func TestCountMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } diff --git a/libgo/go/strings/indexbyte.c b/libgo/go/strings/indexbyte.c new file mode 100644 index 0000000..27f4240 --- /dev/null +++ b/libgo/go/strings/indexbyte.c @@ -0,0 +1,29 @@ +/* indexbyte.c -- implement strings.IndexByte for Go. + + 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. */ + +#include <stddef.h> + +#include "runtime.h" +#include "go-string.h" + +/* This is in C so that the compiler can optimize it appropriately. + We deliberately don't split the stack in case it does call the + library function, which shouldn't need much stack space. */ + +intgo IndexByte (String, char) + __asm__ (GOSYM_PREFIX "strings.IndexByte") + __attribute__ ((no_split_stack)); + +intgo +IndexByte (String s, char b) +{ + const char *p; + + p = __builtin_memchr ((const char *) s.str, b, s.len); + if (p == NULL) + return -1; + return p - (const char *) s.str; +} diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go index 986f6d6..5d46211 100644 --- a/libgo/go/strings/strings.go +++ b/libgo/go/strings/strings.go @@ -130,14 +130,7 @@ func Index(s, sep string) int { case n == 0: return 0 case n == 1: - c := sep[0] - // special case worth making fast - for i := 0; i < len(s); i++ { - if s[i] == c { - return i - } - } - return -1 + return IndexByte(s, sep[0]) case n == len(s): if sep == s { return 0 @@ -432,10 +425,7 @@ func Repeat(s string, count int) string { b := make([]byte, len(s)*count) bp := 0 for i := 0; i < count; i++ { - for j := 0; j < len(s); j++ { - b[bp] = s[j] - bp++ - } + bp += copy(b[bp:], s) } return string(b) } diff --git a/libgo/go/strings/strings_decl.go b/libgo/go/strings/strings_decl.go new file mode 100644 index 0000000..810a696 --- /dev/null +++ b/libgo/go/strings/strings_decl.go @@ -0,0 +1,8 @@ +// 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 strings + +// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s. +func IndexByte(s string, c byte) int // ../runtime/asm_$GOARCH.s diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go index 68b658c..df0dd71 100644 --- a/libgo/go/strings/strings_test.go +++ b/libgo/go/strings/strings_test.go @@ -168,6 +168,15 @@ func BenchmarkIndex(b *testing.B) { } } +func BenchmarkIndexByte(b *testing.B) { + if got := IndexByte(benchmarkString, 'v'); got != 17 { + b.Fatalf("wrong index: expected 17, got=%d", got) + } + for i := 0; i < b.N; i++ { + IndexByte(benchmarkString, 'v') + } +} + var explodetests = []struct { s string n int @@ -1001,6 +1010,30 @@ func TestEqualFold(t *testing.T) { } } +var CountTests = []struct { + s, sep string + num int +}{ + {"", "", 1}, + {"", "notempty", 0}, + {"notempty", "", 9}, + {"smaller", "not smaller", 0}, + {"12345678987654321", "6", 2}, + {"611161116", "6", 3}, + {"notequal", "NotEqual", 0}, + {"equal", "equal", 1}, + {"abc1231231123q", "123", 3}, + {"11111", "11", 2}, +} + +func TestCount(t *testing.T) { + for _, tt := range CountTests { + if num := Count(tt.s, tt.sep); num != tt.num { + t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num) + } + } +} + func makeBenchInputHard() string { tokens := [...]string{ "<a>", "<p>", "<b>", "<strong>", diff --git a/libgo/go/sync/atomic/64bit_arm.go b/libgo/go/sync/atomic/64bit_arm.go index f070e78..c08f214 100644 --- a/libgo/go/sync/atomic/64bit_arm.go +++ b/libgo/go/sync/atomic/64bit_arm.go @@ -34,3 +34,13 @@ func addUint64(val *uint64, delta uint64) (new uint64) { } return } + +func swapUint64(addr *uint64, new uint64) (old uint64) { + for { + old = *addr + if CompareAndSwapUint64(addr, old, new) { + break + } + } + return +} diff --git a/libgo/go/sync/atomic/atomic.c b/libgo/go/sync/atomic/atomic.c index 32430df..f0ba57b 100644 --- a/libgo/go/sync/atomic/atomic.c +++ b/libgo/go/sync/atomic/atomic.c @@ -8,8 +8,69 @@ #include "runtime.h" +int32_t SwapInt32 (int32_t *, int32_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapInt32") + __attribute__ ((no_split_stack)); + +int32_t +SwapInt32 (int32_t *addr, int32_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +int64_t SwapInt64 (int64_t *, int64_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapInt64") + __attribute__ ((no_split_stack)); + +int64_t +SwapInt64 (int64_t *addr, int64_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +uint32_t SwapUint32 (uint32_t *, uint32_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapUint32") + __attribute__ ((no_split_stack)); + +uint32_t +SwapUint32 (uint32_t *addr, uint32_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +uint64_t SwapUint64 (uint64_t *, uint64_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapUint64") + __attribute__ ((no_split_stack)); + +uint64_t +SwapUint64 (uint64_t *addr, uint64_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +uintptr_t SwapUintptr (uintptr_t *, uintptr_t) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapUintptr") + __attribute__ ((no_split_stack)); + +uintptr_t +SwapUintptr (uintptr_t *addr, uintptr_t new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + +void *SwapPointer (void **, void *) + __asm__ (GOSYM_PREFIX "sync_atomic.SwapPointer") + __attribute__ ((no_split_stack)); + +void * +SwapPointer (void **addr, void *new) +{ + return __atomic_exchange_n (addr, new, __ATOMIC_SEQ_CST); +} + _Bool CompareAndSwapInt32 (int32_t *, int32_t, int32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt32") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapInt32 (int32_t *val, int32_t old, int32_t new) @@ -18,7 +79,8 @@ CompareAndSwapInt32 (int32_t *val, int32_t old, int32_t new) } _Bool CompareAndSwapInt64 (int64_t *, int64_t, int64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapInt64") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapInt64 (int64_t *val, int64_t old, int64_t new) @@ -27,7 +89,8 @@ CompareAndSwapInt64 (int64_t *val, int64_t old, int64_t new) } _Bool CompareAndSwapUint32 (uint32_t *, uint32_t, uint32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint32") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapUint32 (uint32_t *val, uint32_t old, uint32_t new) @@ -36,7 +99,8 @@ CompareAndSwapUint32 (uint32_t *val, uint32_t old, uint32_t new) } _Bool CompareAndSwapUint64 (uint64_t *, uint64_t, uint64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUint64") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapUint64 (uint64_t *val, uint64_t old, uint64_t new) @@ -45,7 +109,8 @@ CompareAndSwapUint64 (uint64_t *val, uint64_t old, uint64_t new) } _Bool CompareAndSwapUintptr (uintptr_t *, uintptr_t, uintptr_t) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapUintptr") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapUintptr (uintptr_t *val, uintptr_t old, uintptr_t new) @@ -54,7 +119,8 @@ CompareAndSwapUintptr (uintptr_t *val, uintptr_t old, uintptr_t new) } _Bool CompareAndSwapPointer (void **, void *, void *) - __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapPointer"); + __asm__ (GOSYM_PREFIX "sync_atomic.CompareAndSwapPointer") + __attribute__ ((no_split_stack)); _Bool CompareAndSwapPointer (void **val, void *old, void *new) @@ -63,7 +129,8 @@ CompareAndSwapPointer (void **val, void *old, void *new) } int32_t AddInt32 (int32_t *, int32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddInt32") + __attribute__ ((no_split_stack)); int32_t AddInt32 (int32_t *val, int32_t delta) @@ -72,7 +139,8 @@ AddInt32 (int32_t *val, int32_t delta) } uint32_t AddUint32 (uint32_t *, uint32_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddUint32") + __attribute__ ((no_split_stack)); uint32_t AddUint32 (uint32_t *val, uint32_t delta) @@ -81,7 +149,8 @@ AddUint32 (uint32_t *val, uint32_t delta) } int64_t AddInt64 (int64_t *, int64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddInt64") + __attribute__ ((no_split_stack)); int64_t AddInt64 (int64_t *val, int64_t delta) @@ -90,7 +159,8 @@ AddInt64 (int64_t *val, int64_t delta) } uint64_t AddUint64 (uint64_t *, uint64_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddUint64") + __attribute__ ((no_split_stack)); uint64_t AddUint64 (uint64_t *val, uint64_t delta) @@ -99,7 +169,8 @@ AddUint64 (uint64_t *val, uint64_t delta) } uintptr_t AddUintptr (uintptr_t *, uintptr_t) - __asm__ (GOSYM_PREFIX "sync_atomic.AddUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.AddUintptr") + __attribute__ ((no_split_stack)); uintptr_t AddUintptr (uintptr_t *val, uintptr_t delta) @@ -108,7 +179,8 @@ AddUintptr (uintptr_t *val, uintptr_t delta) } int32_t LoadInt32 (int32_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt32") + __attribute__ ((no_split_stack)); int32_t LoadInt32 (int32_t *addr) @@ -122,7 +194,8 @@ LoadInt32 (int32_t *addr) } int64_t LoadInt64 (int64_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadInt64") + __attribute__ ((no_split_stack)); int64_t LoadInt64 (int64_t *addr) @@ -136,7 +209,8 @@ LoadInt64 (int64_t *addr) } uint32_t LoadUint32 (uint32_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint32") + __attribute__ ((no_split_stack)); uint32_t LoadUint32 (uint32_t *addr) @@ -150,7 +224,8 @@ LoadUint32 (uint32_t *addr) } uint64_t LoadUint64 (uint64_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadUint64") + __attribute__ ((no_split_stack)); uint64_t LoadUint64 (uint64_t *addr) @@ -164,7 +239,8 @@ LoadUint64 (uint64_t *addr) } uintptr_t LoadUintptr (uintptr_t *addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadUintptr") + __attribute__ ((no_split_stack)); uintptr_t LoadUintptr (uintptr_t *addr) @@ -178,7 +254,8 @@ LoadUintptr (uintptr_t *addr) } void *LoadPointer (void **addr) - __asm__ (GOSYM_PREFIX "sync_atomic.LoadPointer"); + __asm__ (GOSYM_PREFIX "sync_atomic.LoadPointer") + __attribute__ ((no_split_stack)); void * LoadPointer (void **addr) @@ -192,7 +269,8 @@ LoadPointer (void **addr) } void StoreInt32 (int32_t *addr, int32_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt32"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt32") + __attribute__ ((no_split_stack)); void StoreInt32 (int32_t *addr, int32_t val) @@ -205,7 +283,8 @@ StoreInt32 (int32_t *addr, int32_t val) } void StoreInt64 (int64_t *addr, int64_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt64"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreInt64") + __attribute__ ((no_split_stack)); void StoreInt64 (int64_t *addr, int64_t val) @@ -218,7 +297,8 @@ StoreInt64 (int64_t *addr, int64_t val) } void StoreUint32 (uint32_t *addr, uint32_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint32"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint32") + __attribute__ ((no_split_stack)); void StoreUint32 (uint32_t *addr, uint32_t val) @@ -231,7 +311,8 @@ StoreUint32 (uint32_t *addr, uint32_t val) } void StoreUint64 (uint64_t *addr, uint64_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint64"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreUint64") + __attribute__ ((no_split_stack)); void StoreUint64 (uint64_t *addr, uint64_t val) @@ -244,7 +325,8 @@ StoreUint64 (uint64_t *addr, uint64_t val) } void StoreUintptr (uintptr_t *addr, uintptr_t val) - __asm__ (GOSYM_PREFIX "sync_atomic.StoreUintptr"); + __asm__ (GOSYM_PREFIX "sync_atomic.StoreUintptr") + __attribute__ ((no_split_stack)); void StoreUintptr (uintptr_t *addr, uintptr_t val) @@ -257,7 +339,8 @@ StoreUintptr (uintptr_t *addr, uintptr_t val) } void StorePointer (void **addr, void *val) - __asm__ (GOSYM_PREFIX "sync_atomic.StorePointer"); + __asm__ (GOSYM_PREFIX "sync_atomic.StorePointer") + __attribute__ ((no_split_stack)); void StorePointer (void **addr, void *val) diff --git a/libgo/go/sync/atomic/atomic_test.go b/libgo/go/sync/atomic/atomic_test.go index c6c33dc..06dd5f7 100644 --- a/libgo/go/sync/atomic/atomic_test.go +++ b/libgo/go/sync/atomic/atomic_test.go @@ -5,7 +5,9 @@ package atomic_test import ( + "fmt" "runtime" + "strings" . "sync/atomic" "testing" "unsafe" @@ -38,6 +40,142 @@ var test64err = func() (err interface{}) { return nil }() +func TestSwapInt32(t *testing.T) { + var x struct { + before int32 + i int32 + after int32 + } + x.before = magic32 + x.after = magic32 + var j int32 + for delta := int32(1); delta+delta > delta; delta += delta { + k := SwapInt32(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + +func TestSwapUint32(t *testing.T) { + var x struct { + before uint32 + i uint32 + after uint32 + } + x.before = magic32 + x.after = magic32 + var j uint32 + for delta := uint32(1); delta+delta > delta; delta += delta { + k := SwapUint32(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic32 || x.after != magic32 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magic32, magic32) + } +} + +func TestSwapInt64(t *testing.T) { + if test64err != nil { + t.Skipf("Skipping 64-bit tests: %v", test64err) + } + var x struct { + before int64 + i int64 + after int64 + } + x.before = magic64 + x.after = magic64 + var j int64 + for delta := int64(1); delta+delta > delta; delta += delta { + k := SwapInt64(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + +func TestSwapUint64(t *testing.T) { + if test64err != nil { + t.Skipf("Skipping 64-bit tests: %v", test64err) + } + var x struct { + before uint64 + i uint64 + after uint64 + } + x.before = magic64 + x.after = magic64 + var j uint64 + for delta := uint64(1); delta+delta > delta; delta += delta { + k := SwapUint64(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magic64 || x.after != magic64 { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, uint64(magic64), uint64(magic64)) + } +} + +func TestSwapUintptr(t *testing.T) { + var x struct { + before uintptr + i uintptr + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + var j uintptr + for delta := uintptr(1); delta+delta > delta; delta += delta { + k := SwapUintptr(&x.i, delta) + if x.i != delta || k != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + +func TestSwapPointer(t *testing.T) { + var x struct { + before uintptr + i unsafe.Pointer + after uintptr + } + var m uint64 = magic64 + magicptr := uintptr(m) + x.before = magicptr + x.after = magicptr + var j uintptr + for delta := uintptr(1); delta+delta > delta; delta += delta { + k := SwapPointer(&x.i, unsafe.Pointer(delta)) + if uintptr(x.i) != delta || uintptr(k) != j { + t.Fatalf("delta=%d i=%d j=%d k=%d", delta, x.i, j, k) + } + j = delta + } + if x.before != magicptr || x.after != magicptr { + t.Fatalf("wrong magic: %#x _ %#x != %#x _ %#x", x.before, x.after, magicptr, magicptr) + } +} + func TestAddInt32(t *testing.T) { var x struct { before int32 @@ -241,7 +379,7 @@ func TestCompareAndSwapInt64(t *testing.T) { } } -func TestCompareAndSwapUint64(t *testing.T) { +func testCompareAndSwapUint64(t *testing.T, cas func(*uint64, uint64, uint64) bool) { if test64err != nil { t.Skipf("Skipping 64-bit tests: %v", test64err) } @@ -254,14 +392,14 @@ func TestCompareAndSwapUint64(t *testing.T) { x.after = magic64 for val := uint64(1); val+val > val; val += val { x.i = val - if !CompareAndSwapUint64(&x.i, val, val+1) { + if !cas(&x.i, val, val+1) { t.Fatalf("should have swapped %#x %#x", val, val+1) } if x.i != val+1 { t.Fatalf("wrong x.i after swap: x.i=%#x val+1=%#x", x.i, val+1) } x.i = val + 1 - if CompareAndSwapUint64(&x.i, val, val+2) { + if cas(&x.i, val, val+2) { t.Fatalf("should not have swapped %#x %#x", val, val+2) } if x.i != val+1 { @@ -273,6 +411,10 @@ func TestCompareAndSwapUint64(t *testing.T) { } } +func TestCompareAndSwapUint64(t *testing.T) { + testCompareAndSwapUint64(t, CompareAndSwapUint64) +} + func TestCompareAndSwapUintptr(t *testing.T) { var x struct { before uintptr @@ -608,27 +750,85 @@ func TestStorePointer(t *testing.T) { // uses the atomic operation to add 1 to a value. After running // multiple hammers in parallel, check that we end with the correct // total. - -var hammer32 = []struct { - name string - f func(*uint32, int) -}{ - {"AddInt32", hammerAddInt32}, - {"AddUint32", hammerAddUint32}, - {"AddUintptr", hammerAddUintptr32}, - {"CompareAndSwapInt32", hammerCompareAndSwapInt32}, - {"CompareAndSwapUint32", hammerCompareAndSwapUint32}, - {"CompareAndSwapUintptr", hammerCompareAndSwapUintptr32}, - {"CompareAndSwapPointer", hammerCompareAndSwapPointer32}, +// Swap can't add 1, so it uses a different scheme. +// The functions repeatedly generate a pseudo-random number such that +// low bits are equal to high bits, swap, check that the old value +// has low and high bits equal. + +var hammer32 = map[string]func(*uint32, int){ + "SwapInt32": hammerSwapInt32, + "SwapUint32": hammerSwapUint32, + "SwapUintptr": hammerSwapUintptr32, + "SwapPointer": hammerSwapPointer32, + "AddInt32": hammerAddInt32, + "AddUint32": hammerAddUint32, + "AddUintptr": hammerAddUintptr32, + "CompareAndSwapInt32": hammerCompareAndSwapInt32, + "CompareAndSwapUint32": hammerCompareAndSwapUint32, + "CompareAndSwapUintptr": hammerCompareAndSwapUintptr32, + "CompareAndSwapPointer": hammerCompareAndSwapPointer32, } func init() { var v uint64 = 1 << 50 if uintptr(v) != 0 { // 64-bit system; clear uintptr tests - hammer32[2].f = nil - hammer32[5].f = nil - hammer32[6].f = nil + delete(hammer32, "SwapUintptr") + delete(hammer32, "SwapPointer") + delete(hammer32, "AddUintptr") + delete(hammer32, "CompareAndSwapUintptr") + delete(hammer32, "CompareAndSwapPointer") + } +} + +func hammerSwapInt32(uaddr *uint32, count int) { + addr := (*int32)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16 + old := uint32(SwapInt32(addr, int32(new))) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapInt32 is not atomic: %v", old)) + } + } +} + +func hammerSwapUint32(addr *uint32, count int) { + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint32(seed+i)<<16 | uint32(seed+i)<<16>>16 + old := SwapUint32(addr, new) + if old>>16 != old<<16>>16 { + panic(fmt.Sprintf("SwapUint32 is not atomic: %v", old)) + } + } +} + +func hammerSwapUintptr32(uaddr *uint32, count int) { + // only safe when uintptr is 32-bit. + // not called on 64-bit systems. + addr := (*uintptr)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + 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)) + } + } +} + +func hammerSwapPointer32(uaddr *uint32, count int) { + // only safe when uintptr is 32-bit. + // not called on 64-bit systems. + addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + 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)) + } } } @@ -713,47 +913,103 @@ func TestHammer32(t *testing.T) { } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p)) - for _, tt := range hammer32 { - if tt.f == nil { - continue - } + for name, testf := range hammer32 { c := make(chan int) var val uint32 for i := 0; i < p; i++ { go func() { - tt.f(&val, n) - c <- 1 + defer func() { + if err := recover(); err != nil { + t.Error(err.(string)) + } + c <- 1 + }() + testf(&val, n) }() } for i := 0; i < p; i++ { <-c } - if val != uint32(n)*p { - t.Fatalf("%s: val=%d want %d", tt.name, val, n*p) + if !strings.HasPrefix(name, "Swap") && val != uint32(n)*p { + t.Fatalf("%s: val=%d want %d", name, val, n*p) } } } -var hammer64 = []struct { - name string - f func(*uint64, int) -}{ - {"AddInt64", hammerAddInt64}, - {"AddUint64", hammerAddUint64}, - {"AddUintptr", hammerAddUintptr64}, - {"CompareAndSwapInt64", hammerCompareAndSwapInt64}, - {"CompareAndSwapUint64", hammerCompareAndSwapUint64}, - {"CompareAndSwapUintptr", hammerCompareAndSwapUintptr64}, - {"CompareAndSwapPointer", hammerCompareAndSwapPointer64}, +var hammer64 = map[string]func(*uint64, int){ + "SwapInt64": hammerSwapInt64, + "SwapUint64": hammerSwapUint64, + "SwapUintptr": hammerSwapUintptr64, + "SwapPointer": hammerSwapPointer64, + "AddInt64": hammerAddInt64, + "AddUint64": hammerAddUint64, + "AddUintptr": hammerAddUintptr64, + "CompareAndSwapInt64": hammerCompareAndSwapInt64, + "CompareAndSwapUint64": hammerCompareAndSwapUint64, + "CompareAndSwapUintptr": hammerCompareAndSwapUintptr64, + "CompareAndSwapPointer": hammerCompareAndSwapPointer64, } func init() { var v uint64 = 1 << 50 if uintptr(v) == 0 { // 32-bit system; clear uintptr tests - hammer64[2].f = nil - hammer64[5].f = nil - hammer64[6].f = nil + delete(hammer64, "SwapUintptr") + delete(hammer64, "SwapPointer") + delete(hammer64, "AddUintptr") + delete(hammer64, "CompareAndSwapUintptr") + delete(hammer64, "CompareAndSwapPointer") + } +} + +func hammerSwapInt64(uaddr *uint64, count int) { + addr := (*int64)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32 + old := uint64(SwapInt64(addr, int64(new))) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapInt64 is not atomic: %v", old)) + } + } +} + +func hammerSwapUint64(addr *uint64, count int) { + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uint64(seed+i)<<32 | uint64(seed+i)<<32>>32 + old := SwapUint64(addr, new) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapUint64 is not atomic: %v", old)) + } + } +} + +func hammerSwapUintptr64(uaddr *uint64, count int) { + // only safe when uintptr is 64-bit. + // not called on 32-bit systems. + addr := (*uintptr)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<32 | uintptr(seed+i)<<32>>32 + old := SwapUintptr(addr, new) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapUintptr is not atomic: %v", old)) + } + } +} + +func hammerSwapPointer64(uaddr *uint64, count int) { + // only safe when uintptr is 64-bit. + // not called on 32-bit systems. + addr := (*unsafe.Pointer)(unsafe.Pointer(uaddr)) + seed := int(uintptr(unsafe.Pointer(&count))) + for i := 0; i < count; i++ { + new := uintptr(seed+i)<<32 | uintptr(seed+i)<<32>>32 + old := uintptr(SwapPointer(addr, unsafe.Pointer(new))) + if old>>32 != old<<32>>32 { + panic(fmt.Sprintf("SwapPointer is not atomic: %v", old)) + } } } @@ -841,23 +1097,25 @@ func TestHammer64(t *testing.T) { } defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(p)) - for _, tt := range hammer64 { - if tt.f == nil { - continue - } + for name, testf := range hammer64 { c := make(chan int) var val uint64 for i := 0; i < p; i++ { go func() { - tt.f(&val, n) - c <- 1 + defer func() { + if err := recover(); err != nil { + t.Error(err.(string)) + } + c <- 1 + }() + testf(&val, n) }() } for i := 0; i < p; i++ { <-c } - if val != uint64(n)*p { - t.Fatalf("%s: val=%d want %d", tt.name, val, n*p) + if !strings.HasPrefix(name, "Swap") && val != uint64(n)*p { + t.Fatalf("%s: val=%d want %d", name, val, n*p) } } } @@ -1205,3 +1463,46 @@ func TestUnaligned64(t *testing.T) { shouldPanic(t, "CompareAndSwapUint64", func() { CompareAndSwapUint64(p, 1, 2) }) shouldPanic(t, "AddUint64", func() { AddUint64(p, 3) }) } + +func TestNilDeref(t *testing.T) { + funcs := [...]func(){ + func() { CompareAndSwapInt32(nil, 0, 0) }, + func() { CompareAndSwapInt64(nil, 0, 0) }, + func() { CompareAndSwapUint32(nil, 0, 0) }, + func() { CompareAndSwapUint64(nil, 0, 0) }, + func() { CompareAndSwapUintptr(nil, 0, 0) }, + func() { CompareAndSwapPointer(nil, nil, nil) }, + func() { SwapInt32(nil, 0) }, + func() { SwapUint32(nil, 0) }, + func() { SwapInt64(nil, 0) }, + func() { SwapUint64(nil, 0) }, + func() { SwapUintptr(nil, 0) }, + func() { SwapPointer(nil, nil) }, + func() { AddInt32(nil, 0) }, + func() { AddUint32(nil, 0) }, + func() { AddInt64(nil, 0) }, + func() { AddUint64(nil, 0) }, + func() { AddUintptr(nil, 0) }, + func() { LoadInt32(nil) }, + func() { LoadInt64(nil) }, + func() { LoadUint32(nil) }, + func() { LoadUint64(nil) }, + func() { LoadUintptr(nil) }, + func() { LoadPointer(nil) }, + func() { StoreInt32(nil, 0) }, + func() { StoreInt64(nil, 0) }, + func() { StoreUint32(nil, 0) }, + func() { StoreUint64(nil, 0) }, + func() { StoreUintptr(nil, 0) }, + func() { StorePointer(nil, nil) }, + } + for _, f := range funcs { + func() { + defer func() { + runtime.GC() + recover() + }() + f() + }() + } +} diff --git a/libgo/go/sync/atomic/doc.go b/libgo/go/sync/atomic/doc.go index 27a12c9..17ba72f 100644 --- a/libgo/go/sync/atomic/doc.go +++ b/libgo/go/sync/atomic/doc.go @@ -13,6 +13,13 @@ // Share memory by communicating; // don't communicate by sharing memory. // +// The swap operation, implemented by the SwapT functions, is the atomic +// equivalent of: +// +// old = *addr +// *addr = new +// return old +// // The compare-and-swap operation, implemented by the CompareAndSwapT // functions, is the atomic equivalent of: // @@ -40,11 +47,31 @@ import ( // BUG(rsc): On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX. // +// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core. +// // On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit // alignment of 64-bit words accessed atomically. The first word in a global // variable or in an allocated struct or slice can be relied upon to be // 64-bit aligned. +// SwapInt32 atomically stores new into *addr and returns the previous *addr value. +func SwapInt32(addr *int32, new int32) (old int32) + +// SwapInt64 atomically stores new into *addr and returns the previous *addr value. +func SwapInt64(addr *int64, new int64) (old int64) + +// SwapUint32 atomically stores new into *addr and returns the previous *addr value. +func SwapUint32(addr *uint32, new uint32) (old uint32) + +// SwapUint64 atomically stores new into *addr and returns the previous *addr value. +func SwapUint64(addr *uint64, new uint64) (old uint64) + +// SwapUintptr atomically stores new into *addr and returns the previous *addr value. +func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) + +// SwapPointer atomically stores new into *addr and returns the previous *addr value. +func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) + // CompareAndSwapInt32 executes the compare-and-swap operation for an int32 value. func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool) @@ -67,12 +94,16 @@ func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapp func AddInt32(addr *int32, delta int32) (new int32) // AddUint32 atomically adds delta to *addr and returns the new value. +// To subtract a signed positive constant value c from x, do AddUint32(&x, ^uint32(c-1)). +// In particular, to decrement x, do AddUint32(&x, ^uint32(0)). func AddUint32(addr *uint32, delta uint32) (new uint32) // AddInt64 atomically adds delta to *addr and returns the new value. func AddInt64(addr *int64, delta int64) (new int64) // AddUint64 atomically adds delta to *addr and returns the new value. +// To subtract a signed positive constant value c from x, do AddUint64(&x, ^uint64(c-1)). +// In particular, to decrement x, do AddUint64(&x, ^uint64(0)). func AddUint64(addr *uint64, delta uint64) (new uint64) // AddUintptr atomically adds delta to *addr and returns the new value. diff --git a/libgo/go/sync/atomic/race.go b/libgo/go/sync/atomic/race.go index 2320b57..6cbbf12 100644 --- a/libgo/go/sync/atomic/race.go +++ b/libgo/go/sync/atomic/race.go @@ -20,6 +20,54 @@ import ( var mtx uint32 = 1 // same for all +func SwapInt32(addr *int32, new int32) (old int32) { + return int32(SwapUint32((*uint32)(unsafe.Pointer(addr)), uint32(new))) +} + +func SwapUint32(addr *uint32, new uint32) (old uint32) { + _ = *addr + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + old = *addr + *addr = new + runtime.RaceReleaseMerge(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) + return +} + +func SwapInt64(addr *int64, new int64) (old int64) { + return int64(SwapUint64((*uint64)(unsafe.Pointer(addr)), uint64(new))) +} + +func SwapUint64(addr *uint64, new uint64) (old uint64) { + _ = *addr + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + old = *addr + *addr = new + runtime.RaceReleaseMerge(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) + return +} + +func SwapUintptr(addr *uintptr, new uintptr) (old uintptr) { + return uintptr(SwapPointer((*unsafe.Pointer)(unsafe.Pointer(addr)), unsafe.Pointer(new))) +} + +func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer) { + _ = *addr + runtime.RaceSemacquire(&mtx) + runtime.RaceRead(unsafe.Pointer(addr)) + runtime.RaceAcquire(unsafe.Pointer(addr)) + old = *addr + *addr = new + runtime.RaceReleaseMerge(unsafe.Pointer(addr)) + runtime.RaceSemrelease(&mtx) + return +} + func CompareAndSwapInt32(val *int32, old, new int32) bool { return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new)) } diff --git a/libgo/go/sync/cond.go b/libgo/go/sync/cond.go index 13547a8..9e6bc17 100644 --- a/libgo/go/sync/cond.go +++ b/libgo/go/sync/cond.go @@ -4,6 +4,11 @@ package sync +import ( + "sync/atomic" + "unsafe" +) + // Cond implements a condition variable, a rendezvous point // for goroutines waiting for or announcing the occurrence // of an event. @@ -11,27 +16,16 @@ package sync // Each Cond has an associated Locker L (often a *Mutex or *RWMutex), // which must be held when changing the condition and // when calling the Wait method. +// +// A Cond can be created as part of other structures. +// A Cond must not be copied after first use. type Cond struct { - L Locker // held while observing or changing the condition - m Mutex // held to avoid internal races - - // We must be careful to make sure that when Signal - // releases a semaphore, the corresponding acquire is - // executed by a goroutine that was already waiting at - // the time of the call to Signal, not one that arrived later. - // To ensure this, we segment waiting goroutines into - // generations punctuated by calls to Signal. Each call to - // Signal begins another generation if there are no goroutines - // left in older generations for it to wake. Because of this - // optimization (only begin another generation if there - // are no older goroutines left), we only need to keep track - // of the two most recent generations, which we call old - // and new. - oldWaiters int // number of waiters in old generation... - oldSema *uint32 // ... waiting on this semaphore + // L is held while observing or changing the condition + L Locker - newWaiters int // number of waiters in new generation... - newSema *uint32 // ... waiting on this semaphore + sema syncSema + waiters uint32 // number of waiters + checker copyChecker } // NewCond returns a new Cond with Locker l. @@ -56,22 +50,16 @@ func NewCond(l Locker) *Cond { // c.L.Unlock() // func (c *Cond) Wait() { + c.checker.check() if raceenabled { - _ = c.m.state raceDisable() } - c.m.Lock() - if c.newSema == nil { - c.newSema = new(uint32) - } - s := c.newSema - c.newWaiters++ - c.m.Unlock() + atomic.AddUint32(&c.waiters, 1) if raceenabled { raceEnable() } c.L.Unlock() - runtime_Semacquire(s) + runtime_Syncsemacquire(&c.sema) c.L.Lock() } @@ -80,26 +68,7 @@ func (c *Cond) Wait() { // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Signal() { - if raceenabled { - _ = c.m.state - raceDisable() - } - c.m.Lock() - if c.oldWaiters == 0 && c.newWaiters > 0 { - // Retire old generation; rename new to old. - c.oldWaiters = c.newWaiters - c.oldSema = c.newSema - c.newWaiters = 0 - c.newSema = nil - } - if c.oldWaiters > 0 { - c.oldWaiters-- - runtime_Semrelease(c.oldSema) - } - c.m.Unlock() - if raceenabled { - raceEnable() - } + c.signalImpl(false) } // Broadcast wakes all goroutines waiting on c. @@ -107,27 +76,43 @@ func (c *Cond) Signal() { // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Broadcast() { + c.signalImpl(true) +} + +func (c *Cond) signalImpl(all bool) { + c.checker.check() if raceenabled { - _ = c.m.state raceDisable() } - c.m.Lock() - // Wake both generations. - if c.oldWaiters > 0 { - for i := 0; i < c.oldWaiters; i++ { - runtime_Semrelease(c.oldSema) + for { + old := atomic.LoadUint32(&c.waiters) + if old == 0 { + if raceenabled { + raceEnable() + } + return } - c.oldWaiters = 0 - } - if c.newWaiters > 0 { - for i := 0; i < c.newWaiters; i++ { - runtime_Semrelease(c.newSema) + new := old - 1 + if all { + new = 0 + } + if atomic.CompareAndSwapUint32(&c.waiters, old, new) { + if raceenabled { + raceEnable() + } + runtime_Syncsemrelease(&c.sema, old-new) + return } - c.newWaiters = 0 - c.newSema = nil } - c.m.Unlock() - if raceenabled { - raceEnable() +} + +// copyChecker holds back pointer to itself to detect object copying. +type copyChecker uintptr + +func (c *copyChecker) check() { + if uintptr(*c) != uintptr(unsafe.Pointer(c)) && + !atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) && + uintptr(*c) != uintptr(unsafe.Pointer(c)) { + panic("sync.Cond is copied") } } diff --git a/libgo/go/sync/cond_test.go b/libgo/go/sync/cond_test.go index cefacb1..467c806 100644 --- a/libgo/go/sync/cond_test.go +++ b/libgo/go/sync/cond_test.go @@ -5,6 +5,8 @@ package sync_test import ( . "sync" + + "runtime" "testing" ) @@ -124,3 +126,130 @@ func TestCondBroadcast(t *testing.T) { } c.Broadcast() } + +func TestRace(t *testing.T) { + x := 0 + c := NewCond(&Mutex{}) + done := make(chan bool) + go func() { + c.L.Lock() + x = 1 + c.Wait() + if x != 2 { + t.Fatal("want 2") + } + x = 3 + c.Signal() + c.L.Unlock() + done <- true + }() + go func() { + c.L.Lock() + for { + if x == 1 { + x = 2 + c.Signal() + break + } + c.L.Unlock() + runtime.Gosched() + c.L.Lock() + } + c.L.Unlock() + done <- true + }() + go func() { + c.L.Lock() + for { + if x == 2 { + c.Wait() + if x != 3 { + t.Fatal("want 3") + } + break + } + if x == 3 { + break + } + c.L.Unlock() + runtime.Gosched() + c.L.Lock() + } + c.L.Unlock() + done <- true + }() + <-done + <-done + <-done +} + +func TestCondCopy(t *testing.T) { + defer func() { + err := recover() + if err == nil || err.(string) != "sync.Cond is copied" { + t.Fatalf("got %v, expect sync.Cond is copied", err) + } + }() + c := Cond{L: &Mutex{}} + c.Signal() + c2 := c + c2.Signal() +} + +func BenchmarkCond1(b *testing.B) { + benchmarkCond(b, 1) +} + +func BenchmarkCond2(b *testing.B) { + benchmarkCond(b, 2) +} + +func BenchmarkCond4(b *testing.B) { + benchmarkCond(b, 4) +} + +func BenchmarkCond8(b *testing.B) { + benchmarkCond(b, 8) +} + +func BenchmarkCond16(b *testing.B) { + benchmarkCond(b, 16) +} + +func BenchmarkCond32(b *testing.B) { + benchmarkCond(b, 32) +} + +func benchmarkCond(b *testing.B, waiters int) { + c := NewCond(&Mutex{}) + done := make(chan bool) + id := 0 + + for routine := 0; routine < waiters+1; routine++ { + go func() { + for i := 0; i < b.N; i++ { + c.L.Lock() + if id == -1 { + c.L.Unlock() + break + } + id++ + if id == waiters+1 { + id = 0 + c.Broadcast() + } else { + c.Wait() + } + c.L.Unlock() + } + c.L.Lock() + id = -1 + c.Broadcast() + c.L.Unlock() + done <- true + }() + } + for routine := 0; routine < waiters+1; routine++ { + <-done + } +} diff --git a/libgo/go/sync/example_test.go b/libgo/go/sync/example_test.go index 031c87f..bdd3af6 100644 --- a/libgo/go/sync/example_test.go +++ b/libgo/go/sync/example_test.go @@ -6,10 +6,15 @@ package sync_test import ( "fmt" - "net/http" "sync" ) +type httpPkg struct{} + +func (httpPkg) Get(url string) {} + +var http httpPkg + // This example fetches several URLs concurrently, // using a WaitGroup to block until all the fetches are complete. func ExampleWaitGroup() { diff --git a/libgo/go/sync/once.go b/libgo/go/sync/once.go index 1699e86..161ae3b 100644 --- a/libgo/go/sync/once.go +++ b/libgo/go/sync/once.go @@ -14,8 +14,8 @@ type Once struct { done uint32 } -// Do calls the function f if and only if the method is being called for the -// first time with this receiver. In other words, given +// Do calls the function f if and only if Do is being called for the +// first time for this instance of Once. In other words, given // var once Once // if once.Do(f) is called multiple times, only the first call will invoke f, // even if f has a different value in each invocation. A new instance of diff --git a/libgo/go/sync/race.go b/libgo/go/sync/race.go index d9431af..fd0277d 100644 --- a/libgo/go/sync/race.go +++ b/libgo/go/sync/race.go @@ -32,3 +32,11 @@ func raceDisable() { func raceEnable() { runtime.RaceEnable() } + +func raceRead(addr unsafe.Pointer) { + runtime.RaceRead(addr) +} + +func raceWrite(addr unsafe.Pointer) { + runtime.RaceWrite(addr) +} diff --git a/libgo/go/sync/race0.go b/libgo/go/sync/race0.go index bef14f9..65ada1c 100644 --- a/libgo/go/sync/race0.go +++ b/libgo/go/sync/race0.go @@ -26,3 +26,9 @@ func raceDisable() { func raceEnable() { } + +func raceRead(addr unsafe.Pointer) { +} + +func raceWrite(addr unsafe.Pointer) { +} diff --git a/libgo/go/sync/runtime.go b/libgo/go/sync/runtime.go index e99599c..3bf47ea 100644 --- a/libgo/go/sync/runtime.go +++ b/libgo/go/sync/runtime.go @@ -4,6 +4,8 @@ package sync +import "unsafe" + // defined in package runtime // Semacquire waits until *s > 0 and then atomically decrements it. @@ -16,3 +18,19 @@ func runtime_Semacquire(s *uint32) // It is intended as a simple wakeup primitive for use by the synchronization // library and should not be used directly. func runtime_Semrelease(s *uint32) + +// Opaque representation of SyncSema in runtime/sema.goc. +type syncSema [3]uintptr + +// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s. +func runtime_Syncsemacquire(s *syncSema) + +// Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s. +func runtime_Syncsemrelease(s *syncSema, n uint32) + +// Ensure that sync and runtime agree on size of syncSema. +func runtime_Syncsemcheck(size uintptr) +func init() { + var s syncSema + runtime_Syncsemcheck(unsafe.Sizeof(s)) +} diff --git a/libgo/go/sync/waitgroup.go b/libgo/go/sync/waitgroup.go index ca38837..2268111 100644 --- a/libgo/go/sync/waitgroup.go +++ b/libgo/go/sync/waitgroup.go @@ -43,12 +43,23 @@ type WaitGroup struct { // other event to be waited for. See the WaitGroup example. func (wg *WaitGroup) Add(delta int) { if raceenabled { - _ = wg.m.state - raceReleaseMerge(unsafe.Pointer(wg)) + _ = wg.m.state // trigger nil deref early + if delta < 0 { + // Synchronize decrements with Wait. + raceReleaseMerge(unsafe.Pointer(wg)) + } raceDisable() defer raceEnable() } v := atomic.AddInt32(&wg.counter, int32(delta)) + if raceenabled { + if delta > 0 && v == int32(delta) { + // The first increment must be synchronized with Wait. + // Need to model this as a read, because there can be + // several concurrent wg.counter transitions from 0. + raceRead(unsafe.Pointer(&wg.sema)) + } + } if v < 0 { panic("sync: negative WaitGroup counter") } @@ -72,7 +83,7 @@ func (wg *WaitGroup) Done() { // Wait blocks until the WaitGroup counter is zero. func (wg *WaitGroup) Wait() { if raceenabled { - _ = wg.m.state + _ = wg.m.state // trigger nil deref early raceDisable() } if atomic.LoadInt32(&wg.counter) == 0 { @@ -83,7 +94,7 @@ func (wg *WaitGroup) Wait() { return } wg.m.Lock() - atomic.AddInt32(&wg.waiters, 1) + w := atomic.AddInt32(&wg.waiters, 1) // This code is racing with the unlocked path in Add above. // The code above modifies counter and then reads waiters. // We must modify waiters and then read counter (the opposite order) @@ -101,6 +112,13 @@ func (wg *WaitGroup) Wait() { } return } + if raceenabled && w == 1 { + // Wait must be synchronized with the first Add. + // Need to model this is as a write to race with the read in Add. + // As a consequence, can do the write only for the first waiter, + // otherwise concurrent Waits will race with each other. + raceWrite(unsafe.Pointer(&wg.sema)) + } if wg.sema == nil { wg.sema = new(uint32) } diff --git a/libgo/go/syscall/bpf_bsd.go b/libgo/go/syscall/bpf_bsd.go index f98036c..cc6c1e7 100644 --- a/libgo/go/syscall/bpf_bsd.go +++ b/libgo/go/syscall/bpf_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd // Berkeley packet filter for BSD variants diff --git a/libgo/go/syscall/consistency_unix_test.go b/libgo/go/syscall/consistency_unix_test.go new file mode 100644 index 0000000..73630bc --- /dev/null +++ b/libgo/go/syscall/consistency_unix_test.go @@ -0,0 +1,34 @@ +// 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 + ) +} diff --git a/libgo/go/syscall/env_unix.go b/libgo/go/syscall/env_unix.go index f479471..f64202e 100644 --- a/libgo/go/syscall/env_unix.go +++ b/libgo/go/syscall/env_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // Unix environment variables. diff --git a/libgo/go/syscall/env_windows.go b/libgo/go/syscall/env_windows.go index 39bd502..420b387 100644 --- a/libgo/go/syscall/env_windows.go +++ b/libgo/go/syscall/env_windows.go @@ -28,20 +28,13 @@ func Getenv(key string) (value string, found bool) { n = 0 } } - if n == 0 { - return "", false - } return string(utf16.Decode(b[0:n])), true } func Setenv(key, value string) error { - var v *uint16 - var err error - if len(value) > 0 { - v, err = UTF16PtrFromString(value) - if err != nil { - return err - } + v, err := UTF16PtrFromString(value) + if err != nil { + return err } keyp, err := UTF16PtrFromString(key) if err != nil { diff --git a/libgo/go/syscall/exec_bsd.go b/libgo/go/syscall/exec_bsd.go index a07129c..217e0c8 100644 --- a/libgo/go/syscall/exec_bsd.go +++ b/libgo/go/syscall/exec_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd package syscall @@ -20,12 +20,17 @@ type SysProcAttr struct { Noctty bool // Detach fd 0 from controlling terminal } +// Implemented in runtime package. +func runtime_BeforeFork() +func runtime_AfterFork() + // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. // If a dup or exec fails, write the errno error to pipe. // (Pipe is close-on-exec so if exec succeeds, it will be closed.) // In the child, this function must not acquire any locks, because // they might have been locked at the time of the fork. This means // no rescheduling, no malloc calls, and no new stack segments. +// For the same reason compiler does not race instrument it. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { @@ -53,13 +58,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // About to call fork. // No more allocation or calls of non-assembly functions. + runtime_BeforeFork() r1, err1 = raw_fork() if err1 != 0 { + runtime_AfterFork() return 0, err1 } if r1 != 0 { // parent; return PID + runtime_AfterFork() return int(r1), 0 } diff --git a/libgo/go/syscall/exec_linux.go b/libgo/go/syscall/exec_linux.go index b9d8a74..5d14ec3 100644 --- a/libgo/go/syscall/exec_linux.go +++ b/libgo/go/syscall/exec_linux.go @@ -23,14 +23,20 @@ type SysProcAttr struct { Noctty bool // Detach fd 0 from controlling terminal Ctty int // Controlling TTY fd (Linux only) Pdeathsig Signal // Signal that the process will get when its parent dies (Linux only) + Cloneflags uintptr // Flags for clone calls (Linux only) } +// Implemented in runtime package. +func runtime_BeforeFork() +func runtime_AfterFork() + // Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child. // If a dup or exec fails, write the errno error to pipe. // (Pipe is close-on-exec so if exec succeeds, it will be closed.) // In the child, this function must not acquire any locks, because // they might have been locked at the time of the fork. This means // no rescheduling, no malloc calls, and no new stack segments. +// For the same reason compiler does not race instrument it. // The calls to RawSyscall are okay because they are assembly // functions that do not grow the stack. func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) { @@ -58,13 +64,16 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr // About to call fork. // No more allocation or calls of non-assembly functions. + runtime_BeforeFork() r1, err1 = raw_fork() if err1 != 0 { + runtime_AfterFork() return 0, err1 } if r1 != 0 { // parent; return PID + runtime_AfterFork() return int(r1), 0 } @@ -250,11 +259,6 @@ childerror: for { raw_exit(253) } - - // Calling panic is not actually safe, - // but the for loop above won't break - // and this shuts up the compiler. - panic("unreached") } // Try to open a pipe with O_CLOEXEC set on both file descriptors. diff --git a/libgo/go/syscall/exec_unix.go b/libgo/go/syscall/exec_unix.go index d4aa959..c31b8d5 100644 --- a/libgo/go/syscall/exec_unix.go +++ b/libgo/go/syscall/exec_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // Fork, exec, wait, etc. diff --git a/libgo/go/syscall/passfd_test.go b/libgo/go/syscall/passfd_test.go index 5d9980f..e8ac32d 100644 --- a/libgo/go/syscall/passfd_test.go +++ b/libgo/go/syscall/passfd_test.go @@ -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 linux darwin freebsd netbsd openbsd +// +build linux dragonfly darwin freebsd netbsd openbsd package syscall_test @@ -13,6 +13,7 @@ import ( "net" "os" "os/exec" + "runtime" "syscall" "testing" "time" @@ -26,6 +27,10 @@ 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" { + // TODO(jsing): Figure out why sendmsg is returning EINVAL. + t.Skip("Skipping test on dragonfly") + } if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" { passFDChild() return diff --git a/libgo/go/syscall/race0.go b/libgo/go/syscall/race0.go index e94fb47..b02f882 100644 --- a/libgo/go/syscall/race0.go +++ b/libgo/go/syscall/race0.go @@ -17,3 +17,9 @@ func raceAcquire(addr unsafe.Pointer) { func raceReleaseMerge(addr unsafe.Pointer) { } + +func raceReadRange(addr unsafe.Pointer, len int) { +} + +func raceWriteRange(addr unsafe.Pointer, len int) { +} diff --git a/libgo/go/syscall/rlimit_linux_test.go b/libgo/go/syscall/rlimit_linux_test.go new file mode 100644 index 0000000..4ec720e --- /dev/null +++ b/libgo/go/syscall/rlimit_linux_test.go @@ -0,0 +1,41 @@ +// 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 syscall_test + +import ( + "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 { + 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) + } +} diff --git a/libgo/go/syscall/route_bsd.go b/libgo/go/syscall/route_bsd.go index 62c5ce1..6380735 100644 --- a/libgo/go/syscall/route_bsd.go +++ b/libgo/go/syscall/route_bsd.go @@ -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 freebsd netbsd openbsd +// +build darwin dragonfly freebsd netbsd openbsd // Routing sockets and messages @@ -13,10 +13,14 @@ import "unsafe" // Round the length of a raw sockaddr up to align it properly. func rsaAlignOf(salen int) int { salign := sizeofPtr - // NOTE: It seems like 64-bit Darwin kernel still requires 32-bit - // aligned access to BSD subsystem. - if darwinAMD64 { + // NOTE: It seems like 64-bit Darwin kernel still requires + // 32-bit aligned access to BSD subsystem. Also NetBSD 6 + // kernel and beyond require 64-bit aligned access to routing + // facilities. + if darwin64Bit { salign = 4 + } else if netbsd32Bit { + salign = 8 } if salen == 0 { return salign @@ -142,6 +146,12 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { return nil } b := m.Data[:] + // We still see AF_UNSPEC in socket addresses on some + // platforms. To identify each address family correctly, we + // will use the address family of RTAX_NETMASK as a preferred + // one on the 32-bit NetBSD kernel, also use the length of + // RTAX_NETMASK socket address on the FreeBSD kernel. + preferredFamily := uint8(AF_UNSPEC) for i := uint(0); i < RTAX_MAX; i++ { if m.Header.Addrs&rtaIfaMask&(1<<i) == 0 { continue @@ -149,14 +159,29 @@ func (m *InterfaceAddrMessage) sockaddr() (sas []Sockaddr) { rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) switch i { case RTAX_IFA: + if rsa.Family == AF_UNSPEC { + rsa.Family = preferredFamily + } sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) if err != nil { return nil } sas = append(sas, sa) case RTAX_NETMASK: - if rsa.Family == AF_UNSPEC { - rsa.Family = AF_INET // an old fasion, AF_UNSPEC means AF_INET + switch rsa.Family { + case AF_UNSPEC: + switch rsa.Len { + case SizeofSockaddrInet4: + rsa.Family = AF_INET + case SizeofSockaddrInet6: + rsa.Family = AF_INET6 + default: + rsa.Family = AF_INET // an old fashion, AF_UNSPEC means AF_INET + } + case AF_INET, AF_INET6: + preferredFamily = rsa.Family + default: + return nil } sa, err := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) if err != nil { diff --git a/libgo/go/syscall/route_dragonfly.go b/libgo/go/syscall/route_dragonfly.go new file mode 100644 index 0000000..acad7a2 --- /dev/null +++ b/libgo/go/syscall/route_dragonfly.go @@ -0,0 +1,72 @@ +// 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. + +// Routing sockets and messages for Dragonfly + +package syscall + +import "unsafe" + +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]} + case RTM_IFANNOUNCE: + p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any)) + return &InterfaceAnnounceMessage{Header: p.Header} + case RTM_NEWADDR, RTM_DELADDR: + p := (*InterfaceAddrMessage)(unsafe.Pointer(any)) + return &InterfaceAddrMessage{Header: p.Header, Data: b[SizeofIfaMsghdr:any.Msglen]} + case RTM_NEWMADDR, RTM_DELMADDR: + p := (*InterfaceMulticastAddrMessage)(unsafe.Pointer(any)) + return &InterfaceMulticastAddrMessage{Header: p.Header, Data: b[SizeofIfmaMsghdr:any.Msglen]} + } + return nil +} + +// InterfaceAnnounceMessage represents a routing message containing +// network interface arrival and depature information. +type InterfaceAnnounceMessage struct { + Header IfAnnounceMsghdr +} + +func (m *InterfaceAnnounceMessage) sockaddr() (sas []Sockaddr) { return nil } + +// InterfaceMulticastAddrMessage represents a routing message +// containing network interface address entries. +type InterfaceMulticastAddrMessage struct { + Header IfmaMsghdr + Data []byte +} + +const rtaIfmaMask = RTA_GATEWAY | RTA_IFP | RTA_IFA + +func (m *InterfaceMulticastAddrMessage) sockaddr() (sas []Sockaddr) { + if m.Header.Addrs&rtaIfmaMask == 0 { + return nil + } + b := m.Data[:] + for i := uint(0); i < RTAX_MAX; i++ { + if m.Header.Addrs&rtaIfmaMask&(1<<i) == 0 { + continue + } + rsa := (*RawSockaddr)(unsafe.Pointer(&b[0])) + switch i { + case RTAX_IFA: + sa, e := anyToSockaddr((*RawSockaddrAny)(unsafe.Pointer(rsa))) + if e != nil { + return nil + } + sas = append(sas, sa) + case RTAX_GATEWAY, RTAX_IFP: + // nothing to do + } + b = b[rsaAlignOf(int(rsa.Len)):] + } + return sas +} diff --git a/libgo/go/syscall/security_windows.go b/libgo/go/syscall/security_windows.go index 017b270..b22ecf5 100644 --- a/libgo/go/syscall/security_windows.go +++ b/libgo/go/syscall/security_windows.go @@ -58,6 +58,14 @@ func TranslateAccountName(username string, from, to uint32, initSize int) (strin return UTF16ToString(b), nil } +const ( + // do not reorder + NetSetupUnknownStatus = iota + NetSetupUnjoined + NetSetupWorkgroupName + NetSetupDomainName +) + type UserInfo10 struct { Name *uint16 Comment *uint16 @@ -66,6 +74,7 @@ type UserInfo10 struct { } //sys NetUserGetInfo(serverName *uint16, userName *uint16, level uint32, buf **byte) (neterr error) = netapi32.NetUserGetInfo +//sys NetGetJoinInformation(server *uint16, name **uint16, bufType *uint32) (neterr error) = netapi32.NetGetJoinInformation //sys NetApiBufferFree(buf *byte) (neterr error) = netapi32.NetApiBufferFree const ( diff --git a/libgo/go/syscall/signame.c b/libgo/go/syscall/signame.c index 6f5c2972b..0453c06 100644 --- a/libgo/go/syscall/signame.c +++ b/libgo/go/syscall/signame.c @@ -31,7 +31,7 @@ Signame (intgo sig) s = buf; } len = __builtin_strlen (s); - data = runtime_mallocgc (len, FlagNoPointers, 0, 0); + data = runtime_mallocgc (len, 0, FlagNoScan); __builtin_memcpy (data, s, len); ret.str = data; ret.len = len; diff --git a/libgo/go/syscall/sockcmsg_unix.go b/libgo/go/syscall/sockcmsg_unix.go index 03c1790..5bc4c2a 100644 --- a/libgo/go/syscall/sockcmsg_unix.go +++ b/libgo/go/syscall/sockcmsg_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // Socket control messages @@ -18,7 +18,7 @@ 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 darwinAMD64 { + if darwin64Bit { salign = 4 } // NOTE: Solaris always uses 32-bit alignment, diff --git a/libgo/go/syscall/socket.go b/libgo/go/syscall/socket.go index cc98d6b..29c7055 100644 --- a/libgo/go/syscall/socket.go +++ b/libgo/go/syscall/socket.go @@ -272,6 +272,10 @@ func SetsockoptTimeval(fd, level, opt int, tv *Timeval) (err error) { return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(tv)), Socklen_t(unsafe.Sizeof(*tv))) } +func SetsockoptICMPv6Filter(fd, level, opt int, filter *ICMPv6Filter) error { + return setsockopt(fd, level, opt, (*byte)(unsafe.Pointer(filter)), SizeofICMPv6Filter) +} + type Linger struct { Onoff int32 Linger int32 diff --git a/libgo/go/syscall/syscall_test.go b/libgo/go/syscall/syscall_test.go new file mode 100644 index 0000000..2a39b54 --- /dev/null +++ b/libgo/go/syscall/syscall_test.go @@ -0,0 +1,30 @@ +// 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 syscall_test + +import ( + "syscall" + "testing" +) + +func testSetGetenv(t *testing.T, key, value string) { + err := syscall.Setenv(key, value) + if err != nil { + t.Fatalf("Setenv failed to set %q: %v", value, err) + } + newvalue, found := syscall.Getenv(key) + if !found { + t.Fatalf("Getenv failed to find %v variable (want value %q)", key, value) + } + if newvalue != value { + t.Fatalf("Getenv(%v) = %q; want %q", key, newvalue, value) + } +} + +func TestEnv(t *testing.T) { + testSetGetenv(t, "TESTENV", "AVALUE") + // make sure TESTENV gets set to "", not deleted + testSetGetenv(t, "TESTENV", "") +} diff --git a/libgo/go/syscall/syscall_unix.go b/libgo/go/syscall/syscall_unix.go index fb62681..966090a 100644 --- a/libgo/go/syscall/syscall_unix.go +++ b/libgo/go/syscall/syscall_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package syscall @@ -24,7 +24,10 @@ func c_syscall32(trap int32, a1, a2, a3, a4, a5, a6 int32) int32 //extern syscall func c_syscall64(trap int64, a1, a2, a3, a4, a5, a6 int64) int64 -const darwinAMD64 = runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" +const ( + darwin64Bit = runtime.GOOS == "darwin" && sizeofPtr == 8 + netbsd32Bit = runtime.GOOS == "netbsd" && sizeofPtr == 4 +) // Do a system call. We look at the size of uintptr to see how to pass // the arguments, so that we don't pass a 64-bit value when the function @@ -182,8 +185,13 @@ func (s Signal) String() string { func Read(fd int, p []byte) (n int, err error) { n, err = read(fd, p) - if raceenabled && err == nil { - raceAcquire(unsafe.Pointer(&ioSync)) + if raceenabled { + if n > 0 { + raceWriteRange(unsafe.Pointer(&p[0]), n) + } + if err == nil { + raceAcquire(unsafe.Pointer(&ioSync)) + } } return } @@ -192,7 +200,11 @@ func Write(fd int, p []byte) (n int, err error) { if raceenabled { raceReleaseMerge(unsafe.Pointer(&ioSync)) } - return write(fd, p) + n, err = write(fd, p) + if raceenabled && n > 0 { + raceReadRange(unsafe.Pointer(&p[0]), n) + } + return } var ioSync int64 diff --git a/libgo/go/testing/allocs.go b/libgo/go/testing/allocs.go index d142a33..9ec47bd 100644 --- a/libgo/go/testing/allocs.go +++ b/libgo/go/testing/allocs.go @@ -9,6 +9,7 @@ import ( ) // AllocsPerRun returns the average number of allocations during calls to f. +// Although the return value has type float64, it will always be an integral value. // // To compute the number of allocations, the function will first be run once as // a warm-up. The average number of allocations over the specified number of @@ -36,6 +37,9 @@ func AllocsPerRun(runs int, f func()) (avg float64) { runtime.ReadMemStats(&memstats) mallocs += memstats.Mallocs - // Average the mallocs over the runs (not counting the warm-up) - return float64(mallocs) / float64(runs) + // Average the mallocs over the runs (not counting the warm-up). + // We are forced to return a float64 because the API is silly, but do + // the division as integers so we can ask if AllocsPerRun()==1 + // instead of AllocsPerRun()<2. + return float64(mallocs / uint64(runs)) } diff --git a/libgo/go/testing/benchmark.go b/libgo/go/testing/benchmark.go index 25fb2d6..3473c5b 100644 --- a/libgo/go/testing/benchmark.go +++ b/libgo/go/testing/benchmark.go @@ -138,7 +138,7 @@ func max(x, y int) int { func roundDown10(n int) int { var tens = 0 // tens = floor(log_10(n)) - for n > 10 { + for n >= 10 { n = n / 10 tens++ } @@ -153,13 +153,16 @@ func roundDown10(n int) int { // roundUp rounds x up to a number of the form [1eX, 2eX, 5eX]. func roundUp(n int) int { base := roundDown10(n) - if n < (2 * base) { + switch { + case n <= base: + return base + case n <= (2 * base): return 2 * base - } - if n < (5 * base) { + case n <= (5 * base): return 5 * base + default: + return 10 * base } - return 10 * base } // run times the benchmark function in a separate goroutine. diff --git a/libgo/go/testing/benchmark_test.go b/libgo/go/testing/benchmark_test.go new file mode 100644 index 0000000..94e994d --- /dev/null +++ b/libgo/go/testing/benchmark_test.go @@ -0,0 +1,58 @@ +// 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 testing_test + +import ( + "testing" +) + +var roundDownTests = []struct { + v, expected int +}{ + {1, 1}, + {9, 1}, + {10, 10}, + {11, 10}, + {100, 100}, + {101, 100}, + {999, 100}, + {1000, 1000}, + {1001, 1000}, +} + +func TestRoundDown10(t *testing.T) { + for _, tt := range roundDownTests { + actual := testing.RoundDown10(tt.v) + if tt.expected != actual { + t.Errorf("roundDown10(%d): expected %d, actual %d", tt.v, tt.expected, actual) + } + } +} + +var roundUpTests = []struct { + v, expected int +}{ + {0, 1}, + {1, 1}, + {2, 2}, + {5, 5}, + {9, 10}, + {999, 1000}, + {1000, 1000}, + {1400, 2000}, + {1700, 2000}, + {4999, 5000}, + {5000, 5000}, + {5001, 10000}, +} + +func TestRoundUp(t *testing.T) { + for _, tt := range roundUpTests { + actual := testing.RoundUp(tt.v) + if tt.expected != actual { + t.Errorf("roundUp(%d): expected %d, actual %d", tt.v, tt.expected, actual) + } + } +} diff --git a/libgo/go/testing/cover.go b/libgo/go/testing/cover.go new file mode 100644 index 0000000..dd29364 --- /dev/null +++ b/libgo/go/testing/cover.go @@ -0,0 +1,86 @@ +// 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. + +// Support for test coverage. + +package testing + +import ( + "fmt" + "os" +) + +// CoverBlock records the coverage data for a single basic block. +// NOTE: This struct is internal to the testing infrastructure and may change. +// It is not covered (yet) by the Go 1 compatibility guidelines. +type CoverBlock struct { + Line0 uint32 + Col0 uint16 + Line1 uint32 + Col1 uint16 + Stmts uint16 +} + +var cover Cover + +// Cover records information about test coverage checking. +// NOTE: This struct is internal to the testing infrastructure and may change. +// It is not covered (yet) by the Go 1 compatibility guidelines. +type Cover struct { + Mode string + Counters map[string][]uint32 + Blocks map[string][]CoverBlock + CoveredPackages string +} + +// RegisterCover records the coverage data accumulators for the tests. +// NOTE: This function is internal to the testing infrastructure and may change. +// It is not covered (yet) by the Go 1 compatibility guidelines. +func RegisterCover(c Cover) { + cover = c +} + +// mustBeNil checks the error and, if present, reports it and exits. +func mustBeNil(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "testing: %s\n", err) + os.Exit(2) + } +} + +// coverReport reports the coverage percentage and writes a coverage profile if requested. +func coverReport() { + var f *os.File + var err error + if *coverProfile != "" { + f, err = os.Create(toOutputDir(*coverProfile)) + mustBeNil(err) + fmt.Fprintf(f, "mode: %s\n", cover.Mode) + defer func() { mustBeNil(f.Close()) }() + } + + var active, total int64 + for name, counts := range cover.Counters { + blocks := cover.Blocks[name] + for i, count := range counts { + stmts := int64(blocks[i].Stmts) + total += stmts + if count > 0 { + active += stmts + } + if f != nil { + _, err := fmt.Fprintf(f, "%s:%d.%d,%d.%d %d %d\n", name, + blocks[i].Line0, blocks[i].Col0, + blocks[i].Line1, blocks[i].Col1, + stmts, + count) + mustBeNil(err) + } + } + } + if total == 0 { + total = 1 + } + fmt.Printf("coverage: %.1f%% of statements%s\n", 100*float64(active)/float64(total), cover.CoveredPackages) +} diff --git a/libgo/go/testing/export_test.go b/libgo/go/testing/export_test.go new file mode 100644 index 0000000..89781b4 --- /dev/null +++ b/libgo/go/testing/export_test.go @@ -0,0 +1,10 @@ +// 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 testing + +var ( + RoundDown10 = roundDown10 + RoundUp = roundUp +) diff --git a/libgo/go/testing/quick/quick.go b/libgo/go/testing/quick/quick.go index 761a647..bc79cc3 100644 --- a/libgo/go/testing/quick/quick.go +++ b/libgo/go/testing/quick/quick.go @@ -34,7 +34,7 @@ func randFloat32(rand *rand.Rand) float32 { // randFloat64 generates a random float taking the full range of a float64. func randFloat64(rand *rand.Rand) float64 { - f := rand.Float64() + f := rand.Float64() * math.MaxFloat64 if rand.Int()&1 == 1 { f = -f } @@ -56,90 +56,88 @@ func Value(t reflect.Type, rand *rand.Rand) (value reflect.Value, ok bool) { return m.Generate(rand, complexSize), true } + v := reflect.New(t).Elem() switch concrete := t; concrete.Kind() { case reflect.Bool: - return reflect.ValueOf(rand.Int()&1 == 0), true + v.SetBool(rand.Int()&1 == 0) case reflect.Float32: - return reflect.ValueOf(randFloat32(rand)), true + v.SetFloat(float64(randFloat32(rand))) case reflect.Float64: - return reflect.ValueOf(randFloat64(rand)), true + v.SetFloat(randFloat64(rand)) case reflect.Complex64: - return reflect.ValueOf(complex(randFloat32(rand), randFloat32(rand))), true + v.SetComplex(complex(float64(randFloat32(rand)), float64(randFloat32(rand)))) case reflect.Complex128: - return reflect.ValueOf(complex(randFloat64(rand), randFloat64(rand))), true + v.SetComplex(complex(randFloat64(rand), randFloat64(rand))) case reflect.Int16: - return reflect.ValueOf(int16(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Int32: - return reflect.ValueOf(int32(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Int64: - return reflect.ValueOf(randInt64(rand)), true + v.SetInt(randInt64(rand)) case reflect.Int8: - return reflect.ValueOf(int8(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Int: - return reflect.ValueOf(int(randInt64(rand))), true + v.SetInt(randInt64(rand)) case reflect.Uint16: - return reflect.ValueOf(uint16(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint32: - return reflect.ValueOf(uint32(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint64: - return reflect.ValueOf(uint64(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint8: - return reflect.ValueOf(uint8(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uint: - return reflect.ValueOf(uint(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Uintptr: - return reflect.ValueOf(uintptr(randInt64(rand))), true + v.SetUint(uint64(randInt64(rand))) case reflect.Map: numElems := rand.Intn(complexSize) - m := reflect.MakeMap(concrete) + v.Set(reflect.MakeMap(concrete)) for i := 0; i < numElems; i++ { key, ok1 := Value(concrete.Key(), rand) value, ok2 := Value(concrete.Elem(), rand) if !ok1 || !ok2 { return reflect.Value{}, false } - m.SetMapIndex(key, value) + v.SetMapIndex(key, value) } - return m, true case reflect.Ptr: - v, ok := Value(concrete.Elem(), rand) + elem, ok := Value(concrete.Elem(), rand) if !ok { return reflect.Value{}, false } - p := reflect.New(concrete.Elem()) - p.Elem().Set(v) - return p, true + v.Set(reflect.New(concrete.Elem())) + v.Elem().Set(elem) case reflect.Slice: numElems := rand.Intn(complexSize) - s := reflect.MakeSlice(concrete, numElems, numElems) + v.Set(reflect.MakeSlice(concrete, numElems, numElems)) for i := 0; i < numElems; i++ { - v, ok := Value(concrete.Elem(), rand) + elem, ok := Value(concrete.Elem(), rand) if !ok { return reflect.Value{}, false } - s.Index(i).Set(v) + v.Index(i).Set(elem) } - return s, true case reflect.String: numChars := rand.Intn(complexSize) codePoints := make([]rune, numChars) for i := 0; i < numChars; i++ { codePoints[i] = rune(rand.Intn(0x10ffff)) } - return reflect.ValueOf(string(codePoints)), true + v.SetString(string(codePoints)) case reflect.Struct: - s := reflect.New(t).Elem() - for i := 0; i < s.NumField(); i++ { - v, ok := Value(concrete.Field(i).Type, rand) + for i := 0; i < v.NumField(); i++ { + elem, ok := Value(concrete.Field(i).Type, rand) if !ok { return reflect.Value{}, false } - s.Field(i).Set(v) + v.Field(i).Set(elem) } - return s, true default: return reflect.Value{}, false } + + return v, true } // A Config structure contains options for running a test. diff --git a/libgo/go/testing/quick/quick_test.go b/libgo/go/testing/quick/quick_test.go index a178ec2..36745ae 100644 --- a/libgo/go/testing/quick/quick_test.go +++ b/libgo/go/testing/quick/quick_test.go @@ -13,32 +13,82 @@ import ( func fBool(a bool) bool { return a } +type TestBoolAlias bool + +func fBoolAlias(a TestBoolAlias) TestBoolAlias { return a } + func fFloat32(a float32) float32 { return a } +type TestFloat32Alias float32 + +func fFloat32Alias(a TestFloat32Alias) TestFloat32Alias { return a } + func fFloat64(a float64) float64 { return a } +type TestFloat64Alias float64 + +func fFloat64Alias(a TestFloat64Alias) TestFloat64Alias { return a } + func fComplex64(a complex64) complex64 { return a } +type TestComplex64Alias complex64 + +func fComplex64Alias(a TestComplex64Alias) TestComplex64Alias { return a } + func fComplex128(a complex128) complex128 { return a } +type TestComplex128Alias complex128 + +func fComplex128Alias(a TestComplex128Alias) TestComplex128Alias { return a } + func fInt16(a int16) int16 { return a } +type TestInt16Alias int16 + +func fInt16Alias(a TestInt16Alias) TestInt16Alias { return a } + func fInt32(a int32) int32 { return a } +type TestInt32Alias int32 + +func fInt32Alias(a TestInt32Alias) TestInt32Alias { return a } + func fInt64(a int64) int64 { return a } +type TestInt64Alias int64 + +func fInt64Alias(a TestInt64Alias) TestInt64Alias { return a } + func fInt8(a int8) int8 { return a } +type TestInt8Alias int8 + +func fInt8Alias(a TestInt8Alias) TestInt8Alias { return a } + func fInt(a int) int { return a } -func fUInt8(a uint8) uint8 { return a } +type TestIntAlias int + +func fIntAlias(a TestIntAlias) TestIntAlias { return a } func fMap(a map[int]int) map[int]int { return a } +type TestMapAlias map[int]int + +func fMapAlias(a TestMapAlias) TestMapAlias { return a } + func fSlice(a []byte) []byte { return a } +type TestSliceAlias []byte + +func fSliceAlias(a TestSliceAlias) TestSliceAlias { return a } + func fString(a string) string { return a } +type TestStringAlias string + +func fStringAlias(a TestStringAlias) TestStringAlias { return a } + type TestStruct struct { A int B string @@ -46,23 +96,55 @@ type TestStruct struct { func fStruct(a TestStruct) TestStruct { return a } +type TestStructAlias TestStruct + +func fStructAlias(a TestStructAlias) TestStructAlias { return a } + func fUint16(a uint16) uint16 { return a } +type TestUint16Alias uint16 + +func fUint16Alias(a TestUint16Alias) TestUint16Alias { return a } + func fUint32(a uint32) uint32 { return a } +type TestUint32Alias uint32 + +func fUint32Alias(a TestUint32Alias) TestUint32Alias { return a } + func fUint64(a uint64) uint64 { return a } +type TestUint64Alias uint64 + +func fUint64Alias(a TestUint64Alias) TestUint64Alias { return a } + func fUint8(a uint8) uint8 { return a } +type TestUint8Alias uint8 + +func fUint8Alias(a TestUint8Alias) TestUint8Alias { return a } + func fUint(a uint) uint { return a } +type TestUintAlias uint + +func fUintAlias(a TestUintAlias) TestUintAlias { return a } + func fUintptr(a uintptr) uintptr { return a } +type TestUintptrAlias uintptr + +func fUintptrAlias(a TestUintptrAlias) TestUintptrAlias { return a } + func fIntptr(a *int) *int { b := *a return &b } +type TestIntptrAlias *int + +func fIntptrAlias(a TestIntptrAlias) TestIntptrAlias { return a } + func reportError(property string, err error, t *testing.T) { if err != nil { t.Errorf("%s: %s", property, err) @@ -71,30 +153,51 @@ func reportError(property string, err error, t *testing.T) { func TestCheckEqual(t *testing.T) { reportError("fBool", CheckEqual(fBool, fBool, nil), t) + reportError("fBoolAlias", CheckEqual(fBoolAlias, fBoolAlias, nil), t) reportError("fFloat32", CheckEqual(fFloat32, fFloat32, nil), t) + reportError("fFloat32Alias", CheckEqual(fFloat32Alias, fFloat32Alias, nil), t) reportError("fFloat64", CheckEqual(fFloat64, fFloat64, nil), t) + reportError("fFloat64Alias", CheckEqual(fFloat64Alias, fFloat64Alias, nil), t) if runtime.GOARCH != "alpha" { reportError("fComplex64", CheckEqual(fComplex64, fComplex64, nil), t) + reportError("fComplex64Alias", CheckEqual(fComplex64Alias, fComplex64Alias, nil), t) reportError("fComplex128", CheckEqual(fComplex128, fComplex128, nil), t) + reportError("fComplex128Alias", CheckEqual(fComplex128Alias, fComplex128Alias, nil), t) } reportError("fInt16", CheckEqual(fInt16, fInt16, nil), t) + reportError("fInt16Alias", CheckEqual(fInt16Alias, fInt16Alias, nil), t) reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t) + reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t) reportError("fInt64", CheckEqual(fInt64, fInt64, nil), t) + reportError("fInt64Alias", CheckEqual(fInt64Alias, fInt64Alias, nil), t) reportError("fInt8", CheckEqual(fInt8, fInt8, nil), t) + reportError("fInt8Alias", CheckEqual(fInt8Alias, fInt8Alias, nil), t) reportError("fInt", CheckEqual(fInt, fInt, nil), t) - reportError("fUInt8", CheckEqual(fUInt8, fUInt8, nil), t) + reportError("fIntAlias", CheckEqual(fIntAlias, fIntAlias, nil), t) reportError("fInt32", CheckEqual(fInt32, fInt32, nil), t) + reportError("fInt32Alias", CheckEqual(fInt32Alias, fInt32Alias, nil), t) reportError("fMap", CheckEqual(fMap, fMap, nil), t) + reportError("fMapAlias", CheckEqual(fMapAlias, fMapAlias, nil), t) reportError("fSlice", CheckEqual(fSlice, fSlice, nil), t) + reportError("fSliceAlias", CheckEqual(fSliceAlias, fSliceAlias, nil), t) reportError("fString", CheckEqual(fString, fString, nil), t) + reportError("fStringAlias", CheckEqual(fStringAlias, fStringAlias, nil), t) reportError("fStruct", CheckEqual(fStruct, fStruct, nil), t) + reportError("fStructAlias", CheckEqual(fStructAlias, fStructAlias, nil), t) reportError("fUint16", CheckEqual(fUint16, fUint16, nil), t) + reportError("fUint16Alias", CheckEqual(fUint16Alias, fUint16Alias, nil), t) reportError("fUint32", CheckEqual(fUint32, fUint32, nil), t) + reportError("fUint32Alias", CheckEqual(fUint32Alias, fUint32Alias, nil), t) reportError("fUint64", CheckEqual(fUint64, fUint64, nil), t) + reportError("fUint64Alias", CheckEqual(fUint64Alias, fUint64Alias, nil), t) reportError("fUint8", CheckEqual(fUint8, fUint8, nil), t) + reportError("fUint8Alias", CheckEqual(fUint8Alias, fUint8Alias, nil), t) reportError("fUint", CheckEqual(fUint, fUint, nil), t) + reportError("fUintAlias", CheckEqual(fUintAlias, fUintAlias, nil), t) reportError("fUintptr", CheckEqual(fUintptr, fUintptr, nil), t) + reportError("fUintptrAlias", CheckEqual(fUintptrAlias, fUintptrAlias, nil), t) reportError("fIntptr", CheckEqual(fIntptr, fIntptr, nil), t) + reportError("fIntptrAlias", CheckEqual(fIntptrAlias, fIntptrAlias, nil), t) } // This tests that ArbitraryValue is working by checking that all the arbitrary diff --git a/libgo/go/testing/testing.go b/libgo/go/testing/testing.go index 312d287..5019e07 100644 --- a/libgo/go/testing/testing.go +++ b/libgo/go/testing/testing.go @@ -23,10 +23,10 @@ // Functions of the form // func BenchmarkXxx(*testing.B) // are considered benchmarks, and are executed by the "go test" command when -// the -test.bench flag is provided. Benchmarks are run sequentially. +// its -bench flag is provided. Benchmarks are run sequentially. // // For a description of the testing flags, see -// http://golang.org/cmd/go/#Description_of_testing_flags. +// http://golang.org/cmd/go/#hdr-Description_of_testing_flags. // // A sample benchmark function looks like this: // func BenchmarkHello(b *testing.B) { @@ -114,8 +114,15 @@ var ( // full test of the package. short = flag.Bool("test.short", false, "run smaller test suite to save time") + // The directory in which to create profile files and the like. When run from + // "go test", the binary always runs in the source directory for the package; + // this flag lets "go test" tell the binary to write the files in the directory where + // the "go test" command is run. + outputDir = flag.String("test.outputdir", "", "directory in which to write profiles") + // Report as tests are run; default is silent for success. chatty = flag.Bool("test.v", false, "verbose: print additional output") + coverProfile = flag.String("test.coverprofile", "", "write a coverage profile to the named file after execution") match = flag.String("test.run", "", "regular expression to select tests and examples to run") memProfile = flag.String("test.memprofile", "", "write a memory profile to the named file after execution") memProfileRate = flag.Int("test.memprofilerate", 0, "if >=0, sets runtime.MemProfileRate") @@ -189,6 +196,31 @@ func decorate(s string) string { return buf.String() } +// TB is the interface common to T and B. +type TB interface { + Error(args ...interface{}) + Errorf(format string, args ...interface{}) + Fail() + FailNow() + Failed() bool + Fatal(args ...interface{}) + Fatalf(format string, args ...interface{}) + Log(args ...interface{}) + Logf(format string, args ...interface{}) + Skip(args ...interface{}) + SkipNow() + Skipf(format string, args ...interface{}) + Skipped() bool + + // A private method to prevent users implementing the + // interface and so future additions to it will not + // violate Go 1 compatibility. + private() +} + +var _ TB = (*T)(nil) +var _ TB = (*B)(nil) + // T is a type passed to Test functions to manage test state and support formatted test logs. // Logs are accumulated during execution and dumped to standard error when done. type T struct { @@ -197,6 +229,8 @@ type T struct { startParallel chan bool // Parallel tests will wait on this. } +func (c *common) private() {} + // Fail marks the function as having failed but continues execution. func (c *common) Fail() { c.mu.Lock() @@ -323,6 +357,9 @@ func (c *common) Skipped() bool { func (t *T) Parallel() { t.signal <- (*T)(nil) // Release main testing loop <-t.startParallel // Wait for serial tests to finish + // Assuming Parallel is the first thing a test does, which is reasonable, + // reinitialize the test's start time because it's actually starting now. + t.start = time.Now() } // An internal type but exported because it is cross-package; part of the implementation @@ -333,8 +370,6 @@ type InternalTest struct { } func tRunner(t *T, test *InternalTest) { - t.start = time.Now() - // When this goroutine is done, either because test.F(t) // returned normally or because a test failure triggered // a call to runtime.Goexit, record the duration and send @@ -350,6 +385,7 @@ func tRunner(t *T, test *InternalTest) { t.signal <- t }() + t.start = time.Now() test.F(t) } @@ -364,12 +400,12 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, haveExamples = len(examples) > 0 testOk := RunTests(matchString, tests) exampleOk := RunExamples(matchString, examples) + stopAlarm() if !testOk || !exampleOk { fmt.Println("FAIL") os.Exit(1) } fmt.Println("PASS") - stopAlarm() RunBenchmarks(matchString, benchmarks) after() } @@ -466,7 +502,7 @@ func before() { runtime.MemProfileRate = *memProfileRate } if *cpuProfile != "" { - f, err := os.Create(*cpuProfile) + f, err := os.Create(toOutputDir(*cpuProfile)) if err != nil { fmt.Fprintf(os.Stderr, "testing: %s", err) return @@ -481,6 +517,10 @@ func before() { if *blockProfile != "" && *blockProfileRate >= 0 { runtime.SetBlockProfileRate(*blockProfileRate) } + if *coverProfile != "" && cover.Mode == "" { + fmt.Fprintf(os.Stderr, "testing: cannot use -test.coverprofile because test binary was not built with coverage enabled\n") + os.Exit(2) + } } // after runs after all testing. @@ -489,27 +529,60 @@ func after() { pprof.StopCPUProfile() // flushes profile to disk } if *memProfile != "" { - f, err := os.Create(*memProfile) + f, err := os.Create(toOutputDir(*memProfile)) if err != nil { - fmt.Fprintf(os.Stderr, "testing: %s", err) - return + fmt.Fprintf(os.Stderr, "testing: %s\n", err) + os.Exit(2) } if err = pprof.WriteHeapProfile(f); err != nil { - fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *memProfile, err) + fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *memProfile, err) + os.Exit(2) } f.Close() } if *blockProfile != "" && *blockProfileRate >= 0 { - f, err := os.Create(*blockProfile) + f, err := os.Create(toOutputDir(*blockProfile)) if err != nil { - fmt.Fprintf(os.Stderr, "testing: %s", err) - return + fmt.Fprintf(os.Stderr, "testing: %s\n", err) + os.Exit(2) } if err = pprof.Lookup("block").WriteTo(f, 0); err != nil { - fmt.Fprintf(os.Stderr, "testing: can't write %s: %s", *blockProfile, err) + fmt.Fprintf(os.Stderr, "testing: can't write %s: %s\n", *blockProfile, err) + os.Exit(2) } f.Close() } + if cover.Mode != "" { + coverReport() + } +} + +// toOutputDir returns the file name relocated, if required, to outputDir. +// Simple implementation to avoid pulling in path/filepath. +func toOutputDir(path string) string { + if *outputDir == "" || path == "" { + return path + } + if runtime.GOOS == "windows" { + // On Windows, it's clumsy, but we can be almost always correct + // by just looking for a drive letter and a colon. + // Absolute paths always have a drive letter (ignoring UNC). + // Problem: if path == "C:A" and outputdir == "C:\Go" it's unclear + // what to do, but even then path/filepath doesn't help. + // TODO: Worth doing better? Probably not, because we're here only + // under the management of go test. + if len(path) >= 2 { + letter, colon := path[0], path[1] + if ('a' <= letter && letter <= 'z' || 'A' <= letter && letter <= 'Z') && colon == ':' { + // If path starts with a drive letter we're stuck with it regardless. + return path + } + } + } + if os.IsPathSeparator(path[0]) { + return path + } + return fmt.Sprintf("%s%c%s", *outputDir, os.PathSeparator, path) } var timer *time.Timer @@ -517,7 +590,9 @@ var timer *time.Timer // startAlarm starts an alarm if requested. func startAlarm() { if *timeout > 0 { - timer = time.AfterFunc(*timeout, alarm) + timer = time.AfterFunc(*timeout, func() { + panic(fmt.Sprintf("test timed out after %v", *timeout)) + }) } } @@ -528,22 +603,20 @@ func stopAlarm() { } } -// alarm is called if the timeout expires. -func alarm() { - panic("test timed out") -} - func parseCpuList() { - if len(*cpuListStr) == 0 { - cpuList = append(cpuList, runtime.GOMAXPROCS(-1)) - } else { - for _, val := range strings.Split(*cpuListStr, ",") { - cpu, err := strconv.Atoi(val) - if err != nil || cpu <= 0 { - fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu", val) - os.Exit(1) - } - cpuList = append(cpuList, cpu) + for _, val := range strings.Split(*cpuListStr, ",") { + val = strings.TrimSpace(val) + if val == "" { + continue + } + cpu, err := strconv.Atoi(val) + if err != nil || cpu <= 0 { + fmt.Fprintf(os.Stderr, "testing: invalid value %q for -test.cpu\n", val) + os.Exit(1) } + cpuList = append(cpuList, cpu) + } + if cpuList == nil { + cpuList = append(cpuList, runtime.GOMAXPROCS(-1)) } } diff --git a/libgo/go/text/template/doc.go b/libgo/go/text/template/doc.go index 2da339c..f622ac7d 100644 --- a/libgo/go/text/template/doc.go +++ b/libgo/go/text/template/doc.go @@ -44,7 +44,8 @@ data, defined in detail below. */ // {{/* a comment */}} // A comment; discarded. May contain newlines. -// Comments do not nest. +// Comments do not nest and must start and end at the +// delimiters, as shown here. /* {{pipeline}} @@ -62,6 +63,12 @@ data, defined in detail below. If the value of the pipeline is empty, T0 is executed; otherwise, T1 is executed. Dot is unaffected. + {{if pipeline}} T1 {{else if pipeline}} T0 {{end}} + To simplify the appearance of if-else chains, the else action + of an if may include another if directly; the effect is exactly + the same as writing + {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}} + {{range pipeline}} T1 {{end}} The value of the pipeline must be an array, slice, map, or channel. If the value of the pipeline has length zero, nothing is output; @@ -300,8 +307,41 @@ Predefined global functions are named as follows. Returns the escaped value of the textual representation of its arguments in a form suitable for embedding in a URL query. -The boolean functions take any zero value to be false and a non-zero value to -be true. +The boolean functions take any zero value to be false and a non-zero +value to be true. + +There is also a set of binary comparison operators defined as +functions: + + eq + Returns the boolean truth of arg1 == arg2 + ne + Returns the boolean truth of arg1 != arg2 + lt + Returns the boolean truth of arg1 < arg2 + le + Returns the boolean truth of arg1 <= arg2 + gt + Returns the boolean truth of arg1 > arg2 + ge + Returns the boolean truth of arg1 >= arg2 + +For simpler multi-way equality tests, eq (only) accepts two or more +arguments and compares the second and subsequent to the first, +returning in effect + + arg1==arg2 || arg1==arg3 || arg1==arg4 ... + +(Unlike with || in Go, however, eq is a function call and all the +arguments will be evaluated.) + +The comparison functions work on basic types only (or named basic +types, such as "type Celsius float32"). They implement the Go rules +for comparison of values, except that size and exact type are +ignored, so any integer value may be compared with any other integer +value, any unsigned integer value may be compared with any other +unsigned integer value, and so on. However, as usual, one may not +compare an int with a float32 and so on. Associated templates diff --git a/libgo/go/text/template/exec.go b/libgo/go/text/template/exec.go index 8ec8174..43b0b26 100644 --- a/libgo/go/text/template/exec.go +++ b/libgo/go/text/template/exec.go @@ -201,7 +201,7 @@ func (s *state) walkIfOrWith(typ parse.NodeType, dot reflect.Value, pipe *parse. } } -// isTrue returns whether the value is 'true', in the sense of not the zero of its type, +// isTrue reports whether the value is 'true', in the sense of not the zero of its type, // and whether the value has a meaningful truth value. func isTrue(val reflect.Value) (truth, ok bool) { if !val.IsValid() { @@ -755,12 +755,21 @@ func indirect(v reflect.Value) (rv reflect.Value, isNil bool) { // the template. func (s *state) printValue(n parse.Node, v reflect.Value) { s.at(n) + iface, ok := printableValue(v) + if !ok { + s.errorf("can't print %s of type %s", n, v.Type()) + } + fmt.Fprint(s.wr, iface) +} + +// printableValue returns the, possibly indirected, interface value inside v that +// is best for a call to formatted printer. +func printableValue(v reflect.Value) (interface{}, bool) { if v.Kind() == reflect.Ptr { v, _ = indirect(v) // fmt.Fprint handles nil. } if !v.IsValid() { - fmt.Fprint(s.wr, "<no value>") - return + return "<no value>", true } if !v.Type().Implements(errorType) && !v.Type().Implements(fmtStringerType) { @@ -769,11 +778,11 @@ func (s *state) printValue(n parse.Node, v reflect.Value) { } else { switch v.Kind() { case reflect.Chan, reflect.Func: - s.errorf("can't print %s of type %s", n, v.Type()) + return nil, false } } } - fmt.Fprint(s.wr, v.Interface()) + return v.Interface(), true } // Types to help sort the keys in a map for reproducible output. diff --git a/libgo/go/text/template/exec_test.go b/libgo/go/text/template/exec_test.go index 0ab20ac..f60702d 100644 --- a/libgo/go/text/template/exec_test.go +++ b/libgo/go/text/template/exec_test.go @@ -24,7 +24,7 @@ type T struct { U16 uint16 X string FloatZero float64 - ComplexZero float64 + ComplexZero complex128 // Nested structs. U *U // Struct with String method. @@ -57,6 +57,7 @@ type T struct { Err error // Pointers PI *int + PS *string PSI *[]int NIL *int // Function (not method) @@ -64,6 +65,7 @@ type T struct { VariadicFunc func(...string) string VariadicFuncInt func(int, ...string) string NilOKFunc func(*int) bool + ErrFunc func() (string, error) // Template to test evaluation of templates. Tmpl *Template // Unexported field; cannot be accessed by template. @@ -124,11 +126,13 @@ var tVal = &T{ Str: bytes.NewBuffer([]byte("foozle")), Err: errors.New("erroozle"), PI: newInt(23), + PS: newString("a string"), PSI: newIntSlice(21, 22, 23), BinaryFunc: func(a, b string) string { return fmt.Sprintf("[%s=%s]", a, b) }, VariadicFunc: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") }, VariadicFuncInt: func(a int, s ...string) string { return fmt.Sprint(a, "=<", strings.Join(s, "+"), ">") }, NilOKFunc: func(s *int) bool { return s == nil }, + ErrFunc: func() (string, error) { return "bla", nil }, Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X } @@ -141,9 +145,11 @@ var iVal I = tVal // Helpers for creation. func newInt(n int) *int { - p := new(int) - *p = n - return p + return &n +} + +func newString(s string) *string { + return &s } func newIntSlice(n ...int) *[]int { @@ -280,6 +286,7 @@ var execTests = []execTest{ // Pointers. {"*int", "{{.PI}}", "23", tVal, true}, + {"*string", "{{.PS}}", "a string", tVal, true}, {"*[]int", "{{.PSI}}", "[21 22 23]", tVal, true}, {"*[]int[1]", "{{index .PSI 1}}", "22", tVal, true}, {"NIL", "{{.NIL}}", "<nil>", tVal, true}, @@ -322,6 +329,7 @@ var execTests = []execTest{ {"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true}, {"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true}, {"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true}, + {".ErrFunc", "{{call .ErrFunc}}", "bla", tVal, true}, // Erroneous function calls (check args). {".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false}, @@ -366,6 +374,8 @@ var execTests = []execTest{ {"if map not unset", "{{if not .MXI.none}}ZERO{{else}}NON-ZERO{{end}}", "ZERO", tVal, true}, {"if $x with $y int", "{{if $x := true}}{{with $y := .I}}{{$x}},{{$y}}{{end}}{{end}}", "true,17", tVal, true}, {"if $x with $x int", "{{if $x := true}}{{with $x := .I}}{{$x}},{{end}}{{$x}}{{end}}", "17,true", tVal, true}, + {"if else if", "{{if false}}FALSE{{else if true}}TRUE{{end}}", "TRUE", tVal, true}, + {"if else chain", "{{if eq 1 3}}1{{else if eq 2 3}}2{{else if eq 3 3}}3{{end}}", "3", tVal, true}, // Print etc. {"print", `{{print "hello, print"}}`, "hello, print", tVal, true}, @@ -388,6 +398,7 @@ var execTests = []execTest{ "<script>alert("XSS");</script>", nil, true}, {"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`, "<script>alert("XSS");</script>", nil, true}, + {"html", `{{html .PS}}`, "a string", tVal, true}, // JavaScript. {"js", `{{js .}}`, `It\'d be nice.`, `It'd be nice.`, true}, @@ -860,3 +871,111 @@ func TestMessageForExecuteEmpty(t *testing.T) { t.Fatal(err) } } + +type cmpTest struct { + expr string + truth string + ok bool +} + +var cmpTests = []cmpTest{ + {"eq true true", "true", true}, + {"eq true false", "false", true}, + {"eq 1+2i 1+2i", "true", true}, + {"eq 1+2i 1+3i", "false", true}, + {"eq 1.5 1.5", "true", true}, + {"eq 1.5 2.5", "false", true}, + {"eq 1 1", "true", true}, + {"eq 1 2", "false", true}, + {"eq `xy` `xy`", "true", true}, + {"eq `xy` `xyz`", "false", true}, + {"eq .Xuint .Xuint", "true", true}, + {"eq .Xuint .Yuint", "false", true}, + {"eq 3 4 5 6 3", "true", true}, + {"eq 3 4 5 6 7", "false", true}, + {"ne true true", "false", true}, + {"ne true false", "true", true}, + {"ne 1+2i 1+2i", "false", true}, + {"ne 1+2i 1+3i", "true", true}, + {"ne 1.5 1.5", "false", true}, + {"ne 1.5 2.5", "true", true}, + {"ne 1 1", "false", true}, + {"ne 1 2", "true", true}, + {"ne `xy` `xy`", "false", true}, + {"ne `xy` `xyz`", "true", true}, + {"ne .Xuint .Xuint", "false", true}, + {"ne .Xuint .Yuint", "true", true}, + {"lt 1.5 1.5", "false", true}, + {"lt 1.5 2.5", "true", true}, + {"lt 1 1", "false", true}, + {"lt 1 2", "true", true}, + {"lt `xy` `xy`", "false", true}, + {"lt `xy` `xyz`", "true", true}, + {"lt .Xuint .Xuint", "false", true}, + {"lt .Xuint .Yuint", "true", true}, + {"le 1.5 1.5", "true", true}, + {"le 1.5 2.5", "true", true}, + {"le 2.5 1.5", "false", true}, + {"le 1 1", "true", true}, + {"le 1 2", "true", true}, + {"le 2 1", "false", true}, + {"le `xy` `xy`", "true", true}, + {"le `xy` `xyz`", "true", true}, + {"le `xyz` `xy`", "false", true}, + {"le .Xuint .Xuint", "true", true}, + {"le .Xuint .Yuint", "true", true}, + {"le .Yuint .Xuint", "false", true}, + {"gt 1.5 1.5", "false", true}, + {"gt 1.5 2.5", "false", true}, + {"gt 1 1", "false", true}, + {"gt 2 1", "true", true}, + {"gt 1 2", "false", true}, + {"gt `xy` `xy`", "false", true}, + {"gt `xy` `xyz`", "false", true}, + {"gt .Xuint .Xuint", "false", true}, + {"gt .Xuint .Yuint", "false", true}, + {"gt .Yuint .Xuint", "true", true}, + {"ge 1.5 1.5", "true", true}, + {"ge 1.5 2.5", "false", true}, + {"ge 2.5 1.5", "true", true}, + {"ge 1 1", "true", true}, + {"ge 1 2", "false", true}, + {"ge 2 1", "true", true}, + {"ge `xy` `xy`", "true", true}, + {"ge `xy` `xyz`", "false", true}, + {"ge `xyz` `xy`", "true", true}, + {"ge .Xuint .Xuint", "true", true}, + {"ge .Xuint .Yuint", "false", true}, + {"ge .Yuint .Xuint", "true", true}, + // Errors + {"eq `xy` 1", "", false}, // Different types. + {"lt true true", "", false}, // Unordered types. + {"lt 1+0i 1+0i", "", false}, // Unordered types. +} + +func TestComparison(t *testing.T) { + b := new(bytes.Buffer) + var cmpStruct = struct { + Xuint, Yuint uint + }{3, 4} + for _, test := range cmpTests { + text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr) + tmpl, err := New("empty").Parse(text) + if err != nil { + t.Fatal(err) + } + b.Reset() + err = tmpl.Execute(b, &cmpStruct) + if test.ok && err != nil { + t.Errorf("%s errored incorrectly: %s", test.expr, err) + continue + } + if !test.ok && err == nil { + t.Errorf("%s did not error", test.expr) + continue + } + if b.String() != test.truth { + t.Errorf("%s: want %s; got %s", test.expr, test.truth, b.String()) + } + } +} diff --git a/libgo/go/text/template/funcs.go b/libgo/go/text/template/funcs.go index 8187663..e854122 100644 --- a/libgo/go/text/template/funcs.go +++ b/libgo/go/text/template/funcs.go @@ -6,6 +6,7 @@ package template import ( "bytes" + "errors" "fmt" "io" "net/url" @@ -35,6 +36,14 @@ var builtins = FuncMap{ "printf": fmt.Sprintf, "println": fmt.Sprintln, "urlquery": URLQueryEscaper, + + // Comparisons + "eq": eq, // == + "ge": ge, // >= + "gt": gt, // > + "le": le, // <= + "lt": lt, // < + "ne": ne, // != } var builtinFuncs = createValueFuncs(builtins) @@ -199,7 +208,7 @@ func call(fn interface{}, args ...interface{}) (interface{}, error) { argv[i] = value } result := v.Call(argv) - if len(result) == 2 { + if len(result) == 2 && !result[1].IsNil() { return result[0].Interface(), result[1].Interface().(error) } return result[0].Interface(), nil @@ -248,6 +257,160 @@ func not(arg interface{}) (truth bool) { return !truth } +// Comparison. + +// TODO: Perhaps allow comparison between signed and unsigned integers. + +var ( + errBadComparisonType = errors.New("invalid type for comparison") + errBadComparison = errors.New("incompatible types for comparison") + errNoComparison = errors.New("missing argument for comparison") +) + +type kind int + +const ( + invalidKind kind = iota + boolKind + complexKind + intKind + floatKind + integerKind + stringKind + uintKind +) + +func basicKind(v reflect.Value) (kind, error) { + switch v.Kind() { + case reflect.Bool: + return boolKind, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intKind, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintKind, nil + case reflect.Float32, reflect.Float64: + return floatKind, nil + case reflect.Complex64, reflect.Complex128: + return complexKind, nil + case reflect.String: + return stringKind, nil + } + return invalidKind, errBadComparisonType +} + +// eq evaluates the comparison a == b || a == c || ... +func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + if len(arg2) == 0 { + return false, errNoComparison + } + for _, arg := range arg2 { + v2 := reflect.ValueOf(arg) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + if k1 != k2 { + return false, errBadComparison + } + truth := false + switch k1 { + case boolKind: + truth = v1.Bool() == v2.Bool() + case complexKind: + truth = v1.Complex() == v2.Complex() + case floatKind: + truth = v1.Float() == v2.Float() + case intKind: + truth = v1.Int() == v2.Int() + case stringKind: + truth = v1.String() == v2.String() + case uintKind: + truth = v1.Uint() == v2.Uint() + default: + panic("invalid kind") + } + if truth { + return true, nil + } + } + return false, nil +} + +// ne evaluates the comparison a != b. +func ne(arg1, arg2 interface{}) (bool, error) { + // != is the inverse of ==. + equal, err := eq(arg1, arg2) + return !equal, err +} + +// lt evaluates the comparison a < b. +func lt(arg1, arg2 interface{}) (bool, error) { + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + v2 := reflect.ValueOf(arg2) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + if k1 != k2 { + return false, errBadComparison + } + truth := false + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + panic("invalid kind") + } + return truth, nil +} + +// le evaluates the comparison <= b. +func le(arg1, arg2 interface{}) (bool, error) { + // <= is < or ==. + lessThan, err := lt(arg1, arg2) + if lessThan || err != nil { + return lessThan, err + } + return eq(arg1, arg2) +} + +// gt evaluates the comparison a > b. +func gt(arg1, arg2 interface{}) (bool, error) { + // > is the inverse of <=. + lessOrEqual, err := le(arg1, arg2) + if err != nil { + return false, err + } + return !lessOrEqual, nil +} + +// ge evaluates the comparison a >= b. +func ge(arg1, arg2 interface{}) (bool, error) { + // >= is the inverse of <. + lessThan, err := lt(arg1, arg2) + if err != nil { + return false, err + } + return !lessThan, nil +} + // HTML escaping. var ( @@ -298,15 +461,7 @@ func HTMLEscapeString(s string) string { // HTMLEscaper returns the escaped HTML equivalent of the textual // representation of its arguments. func HTMLEscaper(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - return HTMLEscapeString(s) + return HTMLEscapeString(evalArgs(args)) } // JavaScript escaping. @@ -391,26 +546,35 @@ func jsIsSpecial(r rune) bool { // JSEscaper returns the escaped JavaScript equivalent of the textual // representation of its arguments. func JSEscaper(args ...interface{}) string { - ok := false - var s string - if len(args) == 1 { - s, ok = args[0].(string) - } - if !ok { - s = fmt.Sprint(args...) - } - return JSEscapeString(s) + return JSEscapeString(evalArgs(args)) } // URLQueryEscaper returns the escaped value of the textual representation of // its arguments in a form suitable for embedding in a URL query. func URLQueryEscaper(args ...interface{}) string { - s, ok := "", false + return url.QueryEscape(evalArgs(args)) +} + +// evalArgs formats the list of arguments into a string. It is therefore equivalent to +// fmt.Sprint(args...) +// except that each argument is indirected (if a pointer), as required, +// using the same rules as the default string evaluation during template +// execution. +func evalArgs(args []interface{}) string { + ok := false + var s string + // Fast path for simple common case. if len(args) == 1 { s, ok = args[0].(string) } if !ok { + for i, arg := range args { + a, ok := printableValue(reflect.ValueOf(arg)) + if ok { + args[i] = a + } // else left fmt do its thing + } s = fmt.Sprint(args...) } - return url.QueryEscape(s) + return s } diff --git a/libgo/go/text/template/multi_test.go b/libgo/go/text/template/multi_test.go index bd98bd0..1f6ed5d 100644 --- a/libgo/go/text/template/multi_test.go +++ b/libgo/go/text/template/multi_test.go @@ -33,10 +33,10 @@ var multiParseTests = []multiParseTest{ nil}, {"one", `{{define "foo"}} FOO {{end}}`, noError, []string{"foo"}, - []string{`" FOO "`}}, + []string{" FOO "}}, {"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError, []string{"foo", "bar"}, - []string{`" FOO "`, `" BAR "`}}, + []string{" FOO ", " BAR "}}, // errors {"missing end", `{{define "foo"}} FOO `, hasError, nil, diff --git a/libgo/go/text/template/parse/lex.go b/libgo/go/text/template/parse/lex.go index 23c0cf0..1674aaf 100644 --- a/libgo/go/text/template/parse/lex.go +++ b/libgo/go/text/template/parse/lex.go @@ -243,11 +243,16 @@ func lexLeftDelim(l *lexer) stateFn { // lexComment scans a comment. The left comment marker is known to be present. func lexComment(l *lexer) stateFn { l.pos += Pos(len(leftComment)) - i := strings.Index(l.input[l.pos:], rightComment+l.rightDelim) + i := strings.Index(l.input[l.pos:], rightComment) if i < 0 { return l.errorf("unclosed comment") } - l.pos += Pos(i + len(rightComment) + len(l.rightDelim)) + l.pos += Pos(i + len(rightComment)) + if !strings.HasPrefix(l.input[l.pos:], l.rightDelim) { + return l.errorf("comment ends before closing delimiter") + + } + l.pos += Pos(len(l.rightDelim)) l.ignore() return lexText } diff --git a/libgo/go/text/template/parse/lex_test.go b/libgo/go/text/template/parse/lex_test.go index d2264c9..d251ccf 100644 --- a/libgo/go/text/template/parse/lex_test.go +++ b/libgo/go/text/template/parse/lex_test.go @@ -336,6 +336,16 @@ var lexTests = []lexTest{ {itemText, 0, "hello-"}, {itemError, 0, `unclosed comment`}, }}, + {"text with comment close separted from delim", "hello-{{/* */ }}-world", []item{ + {itemText, 0, "hello-"}, + {itemError, 0, `comment ends before closing delimiter`}, + }}, + // This one is an error that we can't catch because it breaks templates with + // minimized JavaScript. Should have fixed it before Go 1.1. + {"unmatched right delimiter", "hello-{.}}-world", []item{ + {itemText, 0, "hello-{.}}-world"}, + tEOF, + }}, } // collect gathers the emitted items into a slice. diff --git a/libgo/go/text/template/parse/node.go b/libgo/go/text/template/parse/node.go index 9d0d09e..dc6a3bb 100644 --- a/libgo/go/text/template/parse/node.go +++ b/libgo/go/text/template/parse/node.go @@ -13,6 +13,8 @@ import ( "strings" ) +var textFormat = "%s" // Changed to "%q" in tests for better error messages. + // A Node is an element in the parse tree. The interface is trivial. // The interface contains an unexported method so that only // types local to this package can satisfy it. @@ -125,7 +127,7 @@ func newText(pos Pos, text string) *TextNode { } func (t *TextNode) String() string { - return fmt.Sprintf("%q", t.Text) + return fmt.Sprintf(textFormat, t.Text) } func (t *TextNode) Copy() Node { diff --git a/libgo/go/text/template/parse/parse.go b/libgo/go/text/template/parse/parse.go index 802e298..34112fb 100644 --- a/libgo/go/text/template/parse/parse.go +++ b/libgo/go/text/template/parse/parse.go @@ -14,7 +14,6 @@ import ( "runtime" "strconv" "strings" - "unicode" ) // Tree is the representation of a single parsed template. @@ -31,6 +30,19 @@ type Tree struct { vars []string // variables defined at the moment. } +// Copy returns a copy of the Tree. Any parsing state is discarded. +func (t *Tree) Copy() *Tree { + if t == nil { + return nil + } + return &Tree{ + Name: t.Name, + ParseName: t.ParseName, + Root: t.Root.CopyList(), + text: t.text, + } +} + // Parse returns a map from template name to parse.Tree, created by parsing the // templates described in the argument string. The top-level template will be // given the specified name. If an error is encountered, parsing stops and an @@ -200,27 +212,6 @@ func (t *Tree) stopParse() { t.funcs = nil } -// atEOF returns true if, possibly after spaces, we're at EOF. -func (t *Tree) atEOF() bool { - for { - token := t.peek() - switch token.typ { - case itemEOF: - return true - case itemText: - for _, r := range token.val { - if !unicode.IsSpace(r) { - return false - } - } - t.next() // skip spaces. - continue - } - break - } - return false -} - // Parse parses the template definition string to construct a representation of // the template for execution. If either action delimiter string is empty, the // default ("{{" or "}}") is used. Embedded template definitions are added to @@ -431,7 +422,7 @@ func (t *Tree) pipeline(context string) (pipe *PipeNode) { } } -func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { +func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) { defer t.popVars(len(t.vars)) line = t.lex.lineNumber() pipe = t.pipeline(context) @@ -440,6 +431,23 @@ func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, switch next.Type() { case nodeEnd: //done case nodeElse: + if allowElseIf { + // Special case for "else if". If the "else" is followed immediately by an "if", + // the elseControl will have left the "if" token pending. Treat + // {{if a}}_{{else if b}}_{{end}} + // as + // {{if a}}_{{else}}{{if b}}_{{end}}{{end}}. + // To do this, parse the if as usual and stop at it {{end}}; the subsequent{{end}} + // is assumed. This technique works even for long if-else-if chains. + // TODO: Should we allow else-if in with and range? + if t.peek().typ == itemIf { + t.next() // Consume the "if" token. + elseList = newList(next.Position()) + elseList.append(t.ifControl()) + // Do not consume the next item - only one {{end}} required. + break + } + } elseList, next = t.itemList() if next.Type() != nodeEnd { t.errorf("expected end; found %s", next) @@ -453,7 +461,7 @@ func (t *Tree) parseControl(context string) (pos Pos, line int, pipe *PipeNode, // {{if pipeline}} itemList {{else}} itemList {{end}} // If keyword is past. func (t *Tree) ifControl() Node { - return newIf(t.parseControl("if")) + return newIf(t.parseControl(true, "if")) } // Range: @@ -461,7 +469,7 @@ func (t *Tree) ifControl() Node { // {{range pipeline}} itemList {{else}} itemList {{end}} // Range keyword is past. func (t *Tree) rangeControl() Node { - return newRange(t.parseControl("range")) + return newRange(t.parseControl(false, "range")) } // With: @@ -469,7 +477,7 @@ func (t *Tree) rangeControl() Node { // {{with pipeline}} itemList {{else}} itemList {{end}} // If keyword is past. func (t *Tree) withControl() Node { - return newWith(t.parseControl("with")) + return newWith(t.parseControl(false, "with")) } // End: @@ -483,6 +491,12 @@ func (t *Tree) endControl() Node { // {{else}} // Else keyword is past. func (t *Tree) elseControl() Node { + // Special case for "else if". + peek := t.peekNonSpace() + if peek.typ == itemIf { + // We see "{{else if ... " but in effect rewrite it to {{else}}{{if ... ". + return newElse(peek.pos, t.lex.lineNumber()) + } return newElse(t.expect(itemRightDelim, "else").pos, t.lex.lineNumber()) } diff --git a/libgo/go/text/template/parse/parse_test.go b/libgo/go/text/template/parse/parse_test.go index 695c76e..ba1a18e 100644 --- a/libgo/go/text/template/parse/parse_test.go +++ b/libgo/go/text/template/parse/parse_test.go @@ -194,6 +194,10 @@ var parseTests = []parseTest{ `{{if .X}}"hello"{{end}}`}, {"if with else", "{{if .X}}true{{else}}false{{end}}", noError, `{{if .X}}"true"{{else}}"false"{{end}}`}, + {"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError, + `{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`}, + {"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError, + `"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`}, {"simple range", "{{range .X}}hello{{end}}", noError, `{{range .X}}"hello"{{end}}`}, {"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError, @@ -238,6 +242,7 @@ var parseTests = []parseTest{ {"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""}, {"adjacent args", "{{printf 3`x`}}", hasError, ""}, {"adjacent args with .", "{{printf `x`.}}", hasError, ""}, + {"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""}, // Equals (and other chars) do not assignments make (yet). {"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"}, {"bug0b", "{{$x = 1}}{{$x}}", hasError, ""}, @@ -256,6 +261,8 @@ var builtins = map[string]interface{}{ } func testParse(doCopy bool, t *testing.T) { + textFormat = "%q" + defer func() { textFormat = "%s" }() for _, test := range parseTests { tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins) switch { @@ -305,7 +312,7 @@ var isEmptyTests = []isEmptyTest{ {"spaces only", " \t\n \t\n", true}, {"definition", `{{define "x"}}something{{end}}`, true}, {"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true}, - {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n}}", false}, + {"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false}, {"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false}, } @@ -325,6 +332,22 @@ func TestIsEmpty(t *testing.T) { } } +func TestErrorContextWithTreeCopy(t *testing.T) { + tree, err := New("root").Parse("{{if true}}{{end}}", "", "", make(map[string]*Tree), nil) + if err != nil { + t.Fatalf("unexpected tree parse failure: %v", err) + } + treeCopy := tree.Copy() + wantLocation, wantContext := tree.ErrorContext(tree.Root.Nodes[0]) + gotLocation, gotContext := treeCopy.ErrorContext(treeCopy.Root.Nodes[0]) + if wantLocation != gotLocation { + t.Errorf("wrong error location want %q got %q", wantLocation, gotLocation) + } + if wantContext != gotContext { + t.Errorf("wrong error location want %q got %q", wantContext, gotContext) + } +} + // All failures, and the result is a string that must appear in the error message. var errorTests = []parseTest{ // Check line numbers are accurate. diff --git a/libgo/go/time/export_test.go b/libgo/go/time/export_test.go index 130ca8f7..dbd553a 100644 --- a/libgo/go/time/export_test.go +++ b/libgo/go/time/export_test.go @@ -17,3 +17,5 @@ func ForceUSPacificForTesting() { ResetLocalOnceForTest() localOnce.Do(initTestingZone) } + +var ParseTimeZone = parseTimeZone diff --git a/libgo/go/time/format.go b/libgo/go/time/format.go index 7fe0402..6f92c12 100644 --- a/libgo/go/time/format.go +++ b/libgo/go/time/format.go @@ -59,35 +59,39 @@ const ( ) const ( - _ = iota - stdLongMonth = iota + stdNeedDate // "January" - stdMonth // "Jan" - stdNumMonth // "1" - stdZeroMonth // "01" - stdLongWeekDay // "Monday" - stdWeekDay // "Mon" - stdDay // "2" - stdUnderDay // "_2" - stdZeroDay // "02" - stdHour = iota + stdNeedClock // "15" - stdHour12 // "3" - stdZeroHour12 // "03" - stdMinute // "4" - stdZeroMinute // "04" - stdSecond // "5" - stdZeroSecond // "05" - stdLongYear = iota + stdNeedDate // "2006" - stdYear // "06" - stdPM = iota + stdNeedClock // "PM" - stdpm // "pm" - stdTZ = iota // "MST" - stdISO8601TZ // "Z0700" // prints Z for UTC - stdISO8601ColonTZ // "Z07:00" // prints Z for UTC - stdNumTZ // "-0700" // always numeric - stdNumShortTZ // "-07" // always numeric - stdNumColonTZ // "-07:00" // always numeric - stdFracSecond0 // ".0", ".00", ... , trailing zeros included - stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted + _ = iota + stdLongMonth = iota + stdNeedDate // "January" + stdMonth // "Jan" + stdNumMonth // "1" + stdZeroMonth // "01" + stdLongWeekDay // "Monday" + stdWeekDay // "Mon" + stdDay // "2" + stdUnderDay // "_2" + stdZeroDay // "02" + stdHour = iota + stdNeedClock // "15" + stdHour12 // "3" + stdZeroHour12 // "03" + stdMinute // "4" + stdZeroMinute // "04" + stdSecond // "5" + stdZeroSecond // "05" + stdLongYear = iota + stdNeedDate // "2006" + stdYear // "06" + stdPM = iota + stdNeedClock // "PM" + stdpm // "pm" + stdTZ = iota // "MST" + stdISO8601TZ // "Z0700" // prints Z for UTC + stdISO8601SecondsTZ // "Z070000" + stdISO8601ColonTZ // "Z07:00" // prints Z for UTC + stdISO8601ColonSecondsTZ // "Z07:00:00" + stdNumTZ // "-0700" // always numeric + stdNumSecondsTz // "-070000" + stdNumShortTZ // "-07" // always numeric + stdNumColonTZ // "-07:00" // always numeric + stdNumColonSecondsTZ // "-07:00:00" + stdFracSecond0 // ".0", ".00", ... , trailing zeros included + stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted stdNeedDate = 1 << 8 // need month, day, year stdNeedClock = 2 << 8 // need hour, minute, second @@ -98,6 +102,16 @@ 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. +// Its purpose is to prevent matching strings like "Month" when looking for "Mon". +func startsWithLowerCase(str string) bool { + if len(str) == 0 { + return false + } + c := str[0] + return 'a' <= c && c <= 'z' +} + // nextStdChunk finds the first occurrence of a std string in // layout and returns the text before, the std string, and the text after. func nextStdChunk(layout string) (prefix string, std int, suffix string) { @@ -108,7 +122,9 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) { if len(layout) >= i+7 && layout[i:i+7] == "January" { return layout[0:i], stdLongMonth, layout[i+7:] } - return layout[0:i], stdMonth, layout[i+3:] + if !startsWithLowerCase(layout[i+3:]) { + return layout[0:i], stdMonth, layout[i+3:] + } } case 'M': // Monday, Mon, MST @@ -117,7 +133,9 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) { if len(layout) >= i+6 && layout[i:i+6] == "Monday" { return layout[0:i], stdLongWeekDay, layout[i+6:] } - return layout[0:i], stdWeekDay, layout[i+3:] + if !startsWithLowerCase(layout[i+3:]) { + return layout[0:i], stdWeekDay, layout[i+3:] + } } if layout[i:i+3] == "MST" { return layout[0:i], stdTZ, layout[i+3:] @@ -165,7 +183,13 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) { return layout[0:i], stdpm, layout[i+2:] } - case '-': // -0700, -07:00, -07 + case '-': // -070000, -07:00:00, -0700, -07:00, -07 + if len(layout) >= i+7 && layout[i:i+7] == "-070000" { + return layout[0:i], stdNumSecondsTz, layout[i+7:] + } + if len(layout) >= i+9 && layout[i:i+9] == "-07:00:00" { + return layout[0:i], stdNumColonSecondsTZ, layout[i+9:] + } if len(layout) >= i+5 && layout[i:i+5] == "-0700" { return layout[0:i], stdNumTZ, layout[i+5:] } @@ -175,13 +199,21 @@ func nextStdChunk(layout string) (prefix string, std int, suffix string) { if len(layout) >= i+3 && layout[i:i+3] == "-07" { return layout[0:i], stdNumShortTZ, layout[i+3:] } - case 'Z': // Z0700, Z07:00 + + case 'Z': // Z070000, Z07:00:00, Z0700, Z07:00, + if len(layout) >= i+7 && layout[i:i+7] == "Z070000" { + return layout[0:i], stdISO8601SecondsTZ, layout[i+7:] + } + if len(layout) >= i+9 && layout[i:i+9] == "Z07:00:00" { + return layout[0:i], stdISO8601ColonSecondsTZ, layout[i+9:] + } if len(layout) >= i+5 && layout[i:i+5] == "Z0700" { return layout[0:i], stdISO8601TZ, layout[i+5:] } if len(layout) >= i+6 && layout[i:i+6] == "Z07:00" { return layout[0:i], stdISO8601ColonTZ, layout[i+6:] } + case '.': // .000 or .999 - repeated digits for fractional seconds. if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') { ch := layout[i+1] @@ -321,8 +353,8 @@ var atoiError = errors.New("time: invalid number") // Duplicates functionality in strconv, but avoids dependency. func atoi(s string) (x int, err error) { neg := false - if s != "" && s[0] == '-' { - neg = true + if s != "" && (s[0] == '-' || s[0] == '+') { + neg = s[0] == '-' s = s[1:] } q, rem, err := leadingInt(s) @@ -507,17 +539,19 @@ func (t Time) Format(layout string) string { } else { b = append(b, "am"...) } - case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumColonTZ: + case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ: // Ugly special case. We cheat and take the "Z" variants // to mean "the time zone as formatted for ISO 8601". - if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ) { + if offset == 0 && (std == stdISO8601TZ || std == stdISO8601ColonTZ || std == stdISO8601SecondsTZ || std == stdISO8601ColonSecondsTZ) { b = append(b, 'Z') break } zone := offset / 60 // convert to minutes + absoffset := offset if zone < 0 { b = append(b, '-') zone = -zone + absoffset = -absoffset } else { b = append(b, '+') } @@ -526,6 +560,15 @@ func (t Time) Format(layout string) string { b = append(b, ':') } b = appendUint(b, uint(zone%60), '0') + + // append seconds if appropriate + if std == stdISO8601SecondsTZ || std == stdNumSecondsTz || std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ { + if std == stdNumColonSecondsTZ || std == stdISO8601ColonSecondsTZ { + b = append(b, ':') + } + b = appendUint(b, uint(absoffset%60), '0') + } + case stdTZ: if name != "" { b = append(b, name...) @@ -780,7 +823,7 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) // Special case: do we have a fractional second but no // fractional second in the format? if len(value) >= 2 && value[0] == '.' && isDigit(value, 1) { - _, std, _ := nextStdChunk(layout) + _, std, _ = nextStdChunk(layout) std &= stdMask if std == stdFracSecond0 || std == stdFracSecond9 { // Fractional second in the layout; proceed normally @@ -821,13 +864,13 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) default: err = errBad } - case stdISO8601TZ, stdISO8601ColonTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ: + case stdISO8601TZ, stdISO8601ColonTZ, stdISO8601SecondsTZ, stdISO8601ColonSecondsTZ, stdNumTZ, stdNumShortTZ, stdNumColonTZ, stdNumSecondsTz, stdNumColonSecondsTZ: if (std == stdISO8601TZ || std == stdISO8601ColonTZ) && len(value) >= 1 && value[0] == 'Z' { value = value[1:] z = UTC break } - var sign, hour, min string + var sign, hour, min, seconds string if std == stdISO8601ColonTZ || std == stdNumColonTZ { if len(value) < 6 { err = errBad @@ -837,26 +880,45 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) err = errBad break } - sign, hour, min, value = value[0:1], value[1:3], value[4:6], value[6:] + sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], "00", value[6:] } else if std == stdNumShortTZ { if len(value) < 3 { err = errBad break } - sign, hour, min, value = value[0:1], value[1:3], "00", value[3:] + sign, hour, min, seconds, value = value[0:1], value[1:3], "00", "00", value[3:] + } else if std == stdISO8601ColonSecondsTZ || std == stdNumColonSecondsTZ { + if len(value) < 9 { + err = errBad + break + } + if value[3] != ':' || value[6] != ':' { + err = errBad + break + } + sign, hour, min, seconds, value = value[0:1], value[1:3], value[4:6], value[7:9], value[9:] + } else if std == stdISO8601SecondsTZ || std == stdNumSecondsTz { + if len(value) < 7 { + err = errBad + break + } + sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], value[5:7], value[7:] } else { if len(value) < 5 { err = errBad break } - sign, hour, min, value = value[0:1], value[1:3], value[3:5], value[5:] + sign, hour, min, seconds, value = value[0:1], value[1:3], value[3:5], "00", value[5:] } - var hr, mm int + var hr, mm, ss int hr, err = atoi(hour) if err == nil { mm, err = atoi(min) } - zoneOffset = (hr*60 + mm) * 60 // offset is in seconds + if err == nil { + ss, err = atoi(seconds) + } + zoneOffset = (hr*60+mm)*60 + ss // offset is in seconds switch sign[0] { case '+': case '-': @@ -871,25 +933,12 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) value = value[3:] break } - - if len(value) >= 3 && value[2] == 'T' { - p, value = value[0:3], value[3:] - } else if len(value) >= 4 && value[3] == 'T' { - p, value = value[0:4], value[4:] - } else { + n, ok := parseTimeZone(value) + if !ok { err = errBad break } - for i := 0; i < len(p); i++ { - if p[i] < 'A' || 'Z' < p[i] { - err = errBad - } - } - if err != nil { - break - } - // It's a valid format. - zoneName = p + zoneName, value = value[:n], value[n:] case stdFracSecond0: // stdFracSecond0 requires the exact number of digits as specified in @@ -962,7 +1011,11 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) } // Otherwise, create fake zone with unknown offset. - t.loc = FixedZone(zoneName, 0) + if len(zoneName) > 3 && zoneName[:3] == "GMT" { + offset, _ = atoi(zoneName[3:]) // Guaranteed OK by parseGMT. + offset *= 3600 + } + t.loc = FixedZone(zoneName, offset) return t, nil } @@ -970,6 +1023,81 @@ func parse(layout, value string, defaultLocation, local *Location) (Time, error) return Date(year, Month(month), day, hour, min, sec, nsec, defaultLocation), nil } +// parseTimeZone parses a time zone string and returns its length. Time zones +// are human-generated and unpredictable. We can't do precise error checking. +// On the other hand, for a correct parse there must be a time zone at the +// beginning of the string, so it's almost always true that there's one +// there. We look at the beginning of the string for a run of upper-case letters. +// If there are more than 5, it's an error. +// If there are 4 or 5 and the last is a T, it's a time zone. +// If there are 3, it's a time zone. +// Otherwise, other than special cases, it's not a time zone. +// GMT is special because it can have an hour offset. +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" { + return 4, true + } + // Special case 2: GMT may have an hour offset; treat it specially. + if value[:3] == "GMT" { + length = parseGMT(value) + return length, true + } + // How many upper-case letters are there? Need at least three, at most five. + var nUpper int + for nUpper = 0; nUpper < 6; nUpper++ { + if nUpper >= len(value) { + break + } + if c := value[nUpper]; c < 'A' || 'Z' < c { + break + } + } + switch nUpper { + case 0, 1, 2, 6: + return 0, false + case 5: // Must end in T to match. + if value[4] == 'T' { + return 5, true + } + case 4: // Must end in T to match. + if value[3] == 'T' { + return 4, true + } + case 3: + return 3, true + } + return 0, false +} + +// parseGMT parses a GMT time zone. The input string is known to start "GMT". +// The function checks whether that is followed by a sign and a number in the +// range -14 through 12 excluding zero. +func parseGMT(value string) int { + value = value[3:] + if len(value) == 0 { + return 3 + } + sign := value[0] + if sign != '-' && sign != '+' { + return 3 + } + x, rem, err := leadingInt(value[1:]) + if err != nil { + return 3 + } + if sign == '-' { + x = -x + } + if x == 0 || x < -14 || 12 < x { + return 3 + } + return 3 + len(value) - len(rem) +} + func parseNanoseconds(value string, nbytes int) (ns int, rangeErrString string, err error) { if value[0] != '.' { err = errBad @@ -1076,11 +1204,11 @@ func ParseDuration(s string) (Duration, error) { if err != nil { return 0, errors.New("time: invalid duration " + orig) } - scale := 1 + scale := 1.0 for n := pl - len(s); n > 0; n-- { scale *= 10 } - g += float64(x) / float64(scale) + g += float64(x) / scale post = pl != len(s) } if !pre && !post { diff --git a/libgo/go/time/genzabbrs.go b/libgo/go/time/genzabbrs.go new file mode 100644 index 0000000..7c637cb --- /dev/null +++ b/libgo/go/time/genzabbrs.go @@ -0,0 +1,145 @@ +// 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 ignore + +// +// usage: +// +// go run genzabbrs.go | gofmt > $GOROOT/src/pkg/time/zoneinfo_abbrs_windows.go +// + +package main + +import ( + "encoding/xml" + "io/ioutil" + "log" + "net/http" + "os" + "sort" + "text/template" + "time" +) + +// getAbbrs finds timezone abbreviations (standard and daylight saving time) +// for location l. +func getAbbrs(l *time.Location) (st, dt string) { + t := time.Date(time.Now().Year(), 0, 0, 0, 0, 0, 0, l) + abbr1, off1 := t.Zone() + for i := 0; i < 12; i++ { + t = t.AddDate(0, 1, 0) + abbr2, off2 := t.Zone() + if abbr1 != abbr2 { + if off2-off1 < 0 { // southern hemisphere + abbr1, abbr2 = abbr2, abbr1 + } + return abbr1, abbr2 + } + } + return abbr1, abbr1 +} + +type zone struct { + WinName string + UnixName string + StTime string + DSTime string +} + +type zones []*zone + +func (zs zones) Len() int { return len(zs) } +func (zs zones) Swap(i, j int) { zs[i], zs[j] = zs[j], zs[i] } +func (zs zones) Less(i, j int) bool { return zs[i].UnixName < zs[j].UnixName } + +const wzURL = "http://unicode.org/cldr/data/common/supplemental/windowsZones.xml" + +type MapZone struct { + Other string `xml:"other,attr"` + Territory string `xml:"territory,attr"` + Type string `xml:"type,attr"` +} + +type SupplementalData struct { + Zones []MapZone `xml:"windowsZones>mapTimezones>mapZone"` +} + +func readWindowsZones() (zones, error) { + r, err := http.Get(wzURL) + if err != nil { + return nil, err + } + defer r.Body.Close() + + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + + var sd SupplementalData + err = xml.Unmarshal(data, &sd) + if err != nil { + return nil, err + } + zs := make(zones, 0) + for _, z := range sd.Zones { + if z.Territory != "001" { + // to avoid dups. I don't know why. + continue + } + l, err := time.LoadLocation(z.Type) + if err != nil { + return nil, err + } + st, dt := getAbbrs(l) + zs = append(zs, &zone{ + WinName: z.Other, + UnixName: z.Type, + StTime: st, + DSTime: dt, + }) + } + return zs, nil +} + +func main() { + zs, err := readWindowsZones() + if err != nil { + log.Fatal(err) + } + sort.Sort(zs) + var v = struct { + URL string + Zs zones + }{ + wzURL, + zs, + } + err = template.Must(template.New("prog").Parse(prog)).Execute(os.Stdout, v) + if err != nil { + log.Fatal(err) + } +} + +const prog = ` +// 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. + +// generated by genzabbrs.go from +// {{.URL}} + +package time + +type abbr struct { + std string + dst string +} + +var abbrs = map[string]abbr{ +{{range .Zs}} "{{.WinName}}": {"{{.StTime}}", "{{.DSTime}}"}, // {{.UnixName}} +{{end}}} + +` diff --git a/libgo/go/time/internal_test.go b/libgo/go/time/internal_test.go index 918a9f3..87fdd32 100644 --- a/libgo/go/time/internal_test.go +++ b/libgo/go/time/internal_test.go @@ -4,6 +4,11 @@ package time +import ( + "errors" + "runtime" +) + func init() { // force US/Pacific for time zone tests ForceUSPacificForTesting() @@ -11,3 +16,65 @@ func init() { var Interrupt = interrupt var DaysIn = daysIn + +func empty(now int64, arg interface{}) {} + +// Test that a runtimeTimer with a duration so large it overflows +// does not cause other timers to hang. +// +// This test has to be in internal_test.go since it fiddles with +// unexported data structures. +func CheckRuntimeTimerOverflow() error { + // We manually create a runtimeTimer to bypass the overflow + // detection logic in NewTimer: we're testing the underlying + // runtime.addtimer function. + r := &runtimeTimer{ + when: nano() + (1<<63 - 1), + f: empty, + arg: nil, + } + startTimer(r) + + timeout := 100 * Millisecond + if runtime.GOOS == "windows" { + // Allow more time for gobuilder to succeed. + timeout = Second + } + + // Start a goroutine that should send on t.C before the timeout. + t := NewTimer(1) + + defer func() { + // Subsequent tests won't work correctly if we don't stop the + // overflow timer and kick the timer proc back into service. + // + // The timer proc is now sleeping and can only be awoken by + // adding a timer to the *beginning* of the heap. We can't + // wake it up by calling NewTimer since other tests may have + // left timers running that should have expired before ours. + // Instead we zero the overflow timer duration and start it + // once more. + stopTimer(r) + t.Stop() + r.when = 0 + startTimer(r) + }() + + // Try to receive from t.C before the timeout. It will succeed + // iff the previous sleep was able to finish. We're forced to + // spin and yield after trying to receive since we can't start + // any more timers (they might hang due to the same bug we're + // now testing). + stop := Now().Add(timeout) + for { + select { + case <-t.C: + return nil // It worked! + default: + if Now().After(stop) { + return errors.New("runtime timer stuck: overflow in addtimer") + } + runtime.Gosched() + } + } +} diff --git a/libgo/go/time/sleep.go b/libgo/go/time/sleep.go index 591fa27..4f55beb 100644 --- a/libgo/go/time/sleep.go +++ b/libgo/go/time/sleep.go @@ -4,7 +4,8 @@ package time -// Sleep pauses the current goroutine for the duration d. +// Sleep pauses the current goroutine for at least the duration d. +// A negative or zero duration causes Sleep to return immediately. func Sleep(d Duration) func nano() int64 { diff --git a/libgo/go/time/sleep_test.go b/libgo/go/time/sleep_test.go index 762549d..cb09a84 100644 --- a/libgo/go/time/sleep_test.go +++ b/libgo/go/time/sleep_test.go @@ -9,6 +9,7 @@ import ( "fmt" "runtime" "sort" + "sync" "sync/atomic" "testing" . "time" @@ -68,33 +69,94 @@ func TestAfterStress(t *testing.T) { atomic.StoreUint32(&stop, 1) } +func benchmark(b *testing.B, bench func(n int)) { + garbage := make([]*Timer, 1<<17) + 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.StopTimer() + for i := 0; i < len(garbage); i++ { + garbage[i].Stop() + } +} + func BenchmarkAfterFunc(b *testing.B) { - i := b.N - c := make(chan bool) - var f func() - f = func() { - i-- - if i >= 0 { - AfterFunc(0, f) - } else { - c <- true + benchmark(b, func(n int) { + c := make(chan bool) + var f func() + f = func() { + n-- + if n >= 0 { + AfterFunc(0, f) + } else { + c <- true + } } - } - AfterFunc(0, f) - <-c + AfterFunc(0, f) + <-c + }) } func BenchmarkAfter(b *testing.B) { - for i := 0; i < b.N; i++ { - <-After(1) - } + benchmark(b, func(n int) { + for i := 0; i < n; i++ { + <-After(1) + } + }) } func BenchmarkStop(b *testing.B) { - for i := 0; i < b.N; i++ { - NewTimer(1 * Second).Stop() - } + benchmark(b, func(n int) { + for i := 0; i < n; i++ { + NewTimer(1 * Second).Stop() + } + }) +} + +func BenchmarkSimultaneousAfterFunc(b *testing.B) { + benchmark(b, func(n int) { + var wg sync.WaitGroup + wg.Add(n) + for i := 0; i < n; i++ { + AfterFunc(0, wg.Done) + } + wg.Wait() + }) +} + +func BenchmarkStartStop(b *testing.B) { + benchmark(b, func(n int) { + timers := make([]*Timer, n) + for i := 0; i < n; i++ { + timers[i] = AfterFunc(Hour, nil) + } + + for i := 0; i < n; i++ { + timers[i].Stop() + } + }) } func TestAfter(t *testing.T) { @@ -315,3 +377,29 @@ func TestOverflowSleep(t *testing.T) { t.Fatalf("negative timeout didn't fire") } } + +// Test that a panic while deleting a timer does not leave +// the timers mutex held, deadlocking a ticker.Stop in a defer. +func TestIssue5745(t *testing.T) { + ticker := NewTicker(Hour) + defer func() { + // would deadlock here before the fix due to + // lock taken before the segfault. + ticker.Stop() + + if r := recover(); r == nil { + t.Error("Expected panic, but none happened.") + } + }() + + // cause a panic due to a segfault + var timer *Timer + timer.Stop() + t.Error("Should be unreachable.") +} + +func TestOverflowRuntimeTimer(t *testing.T) { + if err := CheckRuntimeTimerOverflow(); err != nil { + t.Fatalf(err.Error()) + } +} diff --git a/libgo/go/time/sys_unix.go b/libgo/go/time/sys_unix.go index 7f69b49..60a3ce0 100644 --- a/libgo/go/time/sys_unix.go +++ b/libgo/go/time/sys_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd package time diff --git a/libgo/go/time/time.go b/libgo/go/time/time.go index d291672..c504df7 100644 --- a/libgo/go/time/time.go +++ b/libgo/go/time/time.go @@ -39,7 +39,14 @@ type Time struct { // nsec specifies a non-negative nanosecond // offset within the second named by Seconds. // It must be in the range [0, 999999999]. - nsec int32 + // + // It is declared as uintptr instead of int32 or uint32 + // to avoid garbage collector aliasing in the case where + // on a 64-bit system the int32 or uint32 field is written + // over the low half of a pointer, creating another pointer. + // TODO(rsc): When the garbage collector is completely + // precise, change back to int32. + nsec uintptr // loc specifies the Location that should be used to // determine the minute, hour, month, day, and year @@ -424,6 +431,11 @@ func (t Time) YearDay() int { // largest representable duration to approximately 290 years. type Duration int64 +const ( + minDuration Duration = -1 << 63 + maxDuration Duration = 1<<63 - 1 +) + // Common durations. There is no definition for units of Day or larger // to avoid confusion across daylight savings time zone transitions. // @@ -600,21 +612,33 @@ func (d Duration) Hours() float64 { // Add returns the time t+d. func (t Time) Add(d Duration) Time { t.sec += int64(d / 1e9) - t.nsec += int32(d % 1e9) - if t.nsec >= 1e9 { + nsec := int32(t.nsec) + int32(d%1e9) + if nsec >= 1e9 { t.sec++ - t.nsec -= 1e9 - } else if t.nsec < 0 { + nsec -= 1e9 + } else if nsec < 0 { t.sec-- - t.nsec += 1e9 + nsec += 1e9 } + t.nsec = uintptr(nsec) return t } -// Sub returns the duration t-u. +// Sub returns the duration t-u. If the result exceeds the maximum (or minimum) +// value that can be stored in a Duration, the maximum (or minimum) duration +// will be returned. // To compute t-d for a duration d, use t.Add(-d). func (t Time) Sub(u Time) Duration { - return Duration(t.sec-u.sec)*Second + Duration(t.nsec-u.nsec) + d := Duration(t.sec-u.sec)*Second + Duration(int32(t.nsec)-int32(u.nsec)) + // Check for overflow or underflow. + switch { + case u.Add(d).Equal(t): + return d // d is correct + case t.Before(u): + return minDuration // t - u is negative out of range + default: + return maxDuration // t - u is positive out of range + } } // Since returns the time elapsed since t. @@ -645,7 +669,6 @@ const ( daysPer400Years = 365*400 + 97 daysPer100Years = 365*100 + 24 daysPer4Years = 365*4 + 1 - days1970To2001 = 31*365 + 8 ) // date computes the year, day of year, and when full=true, @@ -760,7 +783,7 @@ func now() (sec int64, nsec int32) // Now returns the current local time. func Now() Time { sec, nsec := now() - return Time{sec + unixToInternal, nsec, Local} + return Time{sec + unixToInternal, uintptr(nsec), Local} } // UTC returns t with the location set to UTC. @@ -816,10 +839,10 @@ func (t Time) UnixNano() int64 { return (t.sec+internalToUnix)*1e9 + int64(t.nsec) } -const timeGobVersion byte = 1 +const timeBinaryVersion byte = 1 -// GobEncode implements the gob.GobEncoder interface. -func (t Time) GobEncode() ([]byte, error) { +// MarshalBinary implements the encoding.BinaryMarshaler interface. +func (t Time) MarshalBinary() ([]byte, error) { var offsetMin int16 // minutes east of UTC. -1 is UTC. if t.Location() == &utcLoc { @@ -827,17 +850,17 @@ func (t Time) GobEncode() ([]byte, error) { } else { _, offset := t.Zone() if offset%60 != 0 { - return nil, errors.New("Time.GobEncode: zone offset has fractional minute") + return nil, errors.New("Time.MarshalBinary: zone offset has fractional minute") } offset /= 60 if offset < -32768 || offset == -1 || offset > 32767 { - return nil, errors.New("Time.GobEncode: unexpected zone offset") + return nil, errors.New("Time.MarshalBinary: unexpected zone offset") } offsetMin = int16(offset) } enc := []byte{ - timeGobVersion, // byte 0 : version + timeBinaryVersion, // byte 0 : version byte(t.sec >> 56), // bytes 1-8: seconds byte(t.sec >> 48), byte(t.sec >> 40), @@ -857,18 +880,19 @@ func (t Time) GobEncode() ([]byte, error) { return enc, nil } -// GobDecode implements the gob.GobDecoder interface. -func (t *Time) GobDecode(buf []byte) error { +// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. +func (t *Time) UnmarshalBinary(data []byte) error { + buf := data if len(buf) == 0 { - return errors.New("Time.GobDecode: no data") + return errors.New("Time.UnmarshalBinary: no data") } - if buf[0] != timeGobVersion { - return errors.New("Time.GobDecode: unsupported version") + if buf[0] != timeBinaryVersion { + return errors.New("Time.UnmarshalBinary: unsupported version") } if len(buf) != /*version*/ 1+ /*sec*/ 8+ /*nsec*/ 4+ /*zone offset*/ 2 { - return errors.New("Time.GobDecode: invalid length") + return errors.New("Time.UnmarshalBinary: invalid length") } buf = buf[1:] @@ -876,7 +900,7 @@ func (t *Time) GobDecode(buf []byte) error { int64(buf[3])<<32 | int64(buf[2])<<40 | int64(buf[1])<<48 | int64(buf[0])<<56 buf = buf[8:] - t.nsec = int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24 + t.nsec = uintptr(int32(buf[3]) | int32(buf[2])<<8 | int32(buf[1])<<16 | int32(buf[0])<<24) buf = buf[4:] offset := int(int16(buf[1])|int16(buf[0])<<8) * 60 @@ -892,8 +916,22 @@ func (t *Time) GobDecode(buf []byte) error { return nil } +// TODO(rsc): Remove GobEncoder, GobDecoder, MarshalJSON, UnmarshalJSON in Go 2. +// The same semantics will be provided by the generic MarshalBinary, MarshalText, +// UnmarshalBinary, UnmarshalText. + +// GobEncode implements the gob.GobEncoder interface. +func (t Time) GobEncode() ([]byte, error) { + return t.MarshalBinary() +} + +// GobDecode implements the gob.GobDecoder interface. +func (t *Time) GobDecode(data []byte) error { + return t.UnmarshalBinary(data) +} + // MarshalJSON implements the json.Marshaler interface. -// Time is formatted as RFC3339. +// 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 { return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]") @@ -902,13 +940,30 @@ func (t Time) MarshalJSON() ([]byte, error) { } // UnmarshalJSON implements the json.Unmarshaler interface. -// Time is expected in RFC3339 format. +// The time is expected to be a quoted string in RFC 3339 format. func (t *Time) UnmarshalJSON(data []byte) (err error) { // Fractional seconds are handled implicitly by Parse. *t, err = Parse(`"`+RFC3339+`"`, string(data)) return } +// MarshalText implements the encoding.TextMarshaler interface. +// The time is formatted in RFC 3339 format, with sub-second precision added if present. +func (t Time) MarshalText() ([]byte, error) { + if y := t.Year(); y < 0 || y >= 10000 { + return nil, errors.New("Time.MarshalText: year outside of range [0,9999]") + } + return []byte(t.Format(RFC3339Nano)), nil +} + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// The time is expected to be in RFC 3339 format. +func (t *Time) UnmarshalText(data []byte) (err error) { + // Fractional seconds are handled implicitly by Parse. + *t, err = Parse(RFC3339, string(data)) + return +} + // Unix returns the local Time corresponding to the given Unix time, // sec seconds and nsec nanoseconds since January 1, 1970 UTC. // It is valid to pass nsec outside the range [0, 999999999]. @@ -922,7 +977,7 @@ func Unix(sec int64, nsec int64) Time { sec-- } } - return Time{sec + unixToInternal, int32(nsec), Local} + return Time{sec + unixToInternal, uintptr(nsec), Local} } func isLeap(year int) bool { @@ -1031,7 +1086,7 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T unix -= int64(offset) } - return Time{unix + unixToInternal, int32(nsec), loc} + return Time{unix + unixToInternal, uintptr(nsec), loc} } // Truncate returns the result of rounding t down to a multiple of d (since the zero time). @@ -1063,13 +1118,14 @@ func (t Time) Round(d Duration) Time { // but it's still here in case we change our minds. func div(t Time, d Duration) (qmod2 int, r Duration) { neg := false + nsec := int32(t.nsec) if t.sec < 0 { // Operate on absolute value. neg = true t.sec = -t.sec - t.nsec = -t.nsec - if t.nsec < 0 { - t.nsec += 1e9 + nsec = -nsec + if nsec < 0 { + nsec += 1e9 t.sec-- // t.sec >= 1 before the -- so safe } } @@ -1077,14 +1133,14 @@ func div(t Time, d Duration) (qmod2 int, r Duration) { switch { // Special case: 2d divides 1 second. case d < Second && Second%(d+d) == 0: - qmod2 = int(t.nsec/int32(d)) & 1 - r = Duration(t.nsec % int32(d)) + qmod2 = int(nsec/int32(d)) & 1 + r = Duration(nsec % int32(d)) // Special case: d is a multiple of 1 second. case d%Second == 0: d1 := int64(d / Second) qmod2 = int(t.sec/d1) & 1 - r = Duration(t.sec%d1)*Second + Duration(t.nsec) + r = Duration(t.sec%d1)*Second + Duration(nsec) // General case. // This could be faster if more cleverness were applied, @@ -1101,7 +1157,7 @@ func div(t Time, d Duration) (qmod2 int, r Duration) { if u0 < u0x { u1++ } - u0x, u0 = u0, u0+uint64(t.nsec) + u0x, u0 = u0, u0+uint64(nsec) if u0 < u0x { u1++ } diff --git a/libgo/go/time/time_test.go b/libgo/go/time/time_test.go index a0ee37a..22b751c 100644 --- a/libgo/go/time/time_test.go +++ b/libgo/go/time/time_test.go @@ -413,6 +413,8 @@ var formatTests = []FormatTest{ {"am/pm", "3pm", "9pm"}, {"AM/PM", "3PM", "9PM"}, {"two-digit year", "06 01 02", "09 02 04"}, + // Three-letter months and days must not be followed by lower-case letter. + {"Janet", "Hi Janet, the Month is January", "Hi Janet, the Month is February"}, // Time stamps, Fractional seconds. {"Stamp", Stamp, "Feb 4 21:00:57"}, {"StampMilli", StampMilli, "Feb 4 21:00:57.012"}, @@ -505,6 +507,11 @@ var parseTests = []ParseTest{ // Leading zeros in other places should not be taken as fractional seconds. {"zero1", "2006.01.02.15.04.05.0", "2010.02.04.21.00.57.0", false, false, 1, 1}, {"zero2", "2006.01.02.15.04.05.00", "2010.02.04.21.00.57.01", false, false, 1, 2}, + // Month and day names only match when not followed by a lower-case letter. + {"Janet", "Hi Janet, the Month is January: Jan _2 15:04:05 2006", "Hi Janet, the Month is February: Feb 4 21:00:57 2010", false, true, 1, 0}, + + // GMT with offset. + {"GMT-8", UnixDate, "Fri Feb 5 05:00:57 GMT-8 2010", true, true, 1, 0}, // Accept any number of fractional second digits (including none) for .999... // In Go 1, .999... was completely ignored in the format, meaning the first two @@ -659,6 +666,38 @@ func TestFormatAndParse(t *testing.T) { } } +type ParseTimeZoneTest struct { + value string + length int + ok bool +} + +var parseTimeZoneTests = []ParseTimeZoneTest{ + {"gmt hi there", 0, false}, + {"GMT hi there", 3, true}, + {"GMT+12 hi there", 6, true}, + {"GMT+00 hi there", 3, true}, // 0 or 00 is not a legal offset. + {"GMT-5 hi there", 5, true}, + {"GMT-51 hi there", 3, true}, + {"ChST hi there", 4, true}, + {"MSDx", 3, true}, + {"MSDY", 0, false}, // four letters must end in T. + {"ESAST hi", 5, true}, + {"ESASTT hi", 0, false}, // run of upper-case letters too long. + {"ESATY hi", 0, false}, // five letters must end in T. +} + +func TestParseTimeZone(t *testing.T) { + for _, test := range parseTimeZoneTests { + length, ok := ParseTimeZone(test.value) + if ok != test.ok { + t.Errorf("expected %t for %q got %t", test.ok, test.value, ok) + } else if length != test.length { + t.Errorf("expected %d for %q got %d", test.length, test.value, length) + } + } +} + type ParseErrorTest struct { format string value string @@ -781,6 +820,44 @@ func TestMinutesInTimeZone(t *testing.T) { } } +type SecondsTimeZoneOffsetTest struct { + format string + value string + expectedoffset int +} + +var secondsTimeZoneOffsetTests = []SecondsTimeZoneOffsetTest{ + {"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)}, + {"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02-00:34:08", -(34*60 + 8)}, + {"2006-01-02T15:04:05-070000", "1871-01-01T05:33:02+003408", 34*60 + 8}, + {"2006-01-02T15:04:05-07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8}, + {"2006-01-02T15:04:05Z070000", "1871-01-01T05:33:02-003408", -(34*60 + 8)}, + {"2006-01-02T15:04:05Z07:00:00", "1871-01-01T05:33:02+00:34:08", 34*60 + 8}, +} + +func TestParseSecondsInTimeZone(t *testing.T) { + // should accept timezone offsets with seconds like: Zone America/New_York -4:56:02 - LMT 1883 Nov 18 12:03:58 + for _, test := range secondsTimeZoneOffsetTests { + time, err := Parse(test.format, test.value) + if err != nil { + t.Fatal("error parsing date:", err) + } + _, offset := time.Zone() + if offset != test.expectedoffset { + t.Errorf("ZoneOffset = %d, want %d", offset, test.expectedoffset) + } + } +} + +func TestFormatSecondsInTimeZone(t *testing.T) { + d := Date(1871, 9, 17, 20, 4, 26, 0, FixedZone("LMT", -(34*60+8))) + timestr := d.Format("2006-01-02T15:04:05Z070000") + expected := "1871-09-17T20:04:26-003408" + if timestr != expected { + t.Errorf("Got %s, want %s", timestr, expected) + } +} + type ISOWeekTest struct { year int // year month, day int // month and day @@ -1106,9 +1183,9 @@ var invalidEncodingTests = []struct { bytes []byte want string }{ - {[]byte{}, "Time.GobDecode: no data"}, - {[]byte{0, 2, 3}, "Time.GobDecode: unsupported version"}, - {[]byte{1, 2, 3}, "Time.GobDecode: invalid length"}, + {[]byte{}, "Time.UnmarshalBinary: no data"}, + {[]byte{0, 2, 3}, "Time.UnmarshalBinary: unsupported version"}, + {[]byte{1, 2, 3}, "Time.UnmarshalBinary: invalid length"}, } func TestInvalidTimeGob(t *testing.T) { @@ -1118,6 +1195,10 @@ func TestInvalidTimeGob(t *testing.T) { if err == nil || err.Error() != tt.want { t.Errorf("time.GobDecode(%#v) error = %v, want %v", tt.bytes, err, tt.want) } + err = ignored.UnmarshalBinary(tt.bytes) + if err == nil || err.Error() != tt.want { + t.Errorf("time.UnmarshalBinary(%#v) error = %v, want %v", tt.bytes, err, tt.want) + } } } @@ -1125,10 +1206,10 @@ var notEncodableTimes = []struct { time Time want string }{ - {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.GobEncode: zone offset has fractional minute"}, - {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.GobEncode: unexpected zone offset"}, - {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.GobEncode: unexpected zone offset"}, - {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.GobEncode: unexpected zone offset"}, + {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 1)), "Time.MarshalBinary: zone offset has fractional minute"}, + {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -1*60)), "Time.MarshalBinary: unexpected zone offset"}, + {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", -32769*60)), "Time.MarshalBinary: unexpected zone offset"}, + {Date(0, 1, 2, 3, 4, 5, 6, FixedZone("", 32768*60)), "Time.MarshalBinary: unexpected zone offset"}, } func TestNotGobEncodableTime(t *testing.T) { @@ -1137,6 +1218,10 @@ func TestNotGobEncodableTime(t *testing.T) { if err == nil || err.Error() != tt.want { t.Errorf("%v GobEncode error = %v, want %v", tt.time, err, tt.want) } + _, err = tt.time.MarshalBinary() + if err == nil || err.Error() != tt.want { + t.Errorf("%v MarshalBinary error = %v, want %v", tt.time, err, tt.want) + } } } @@ -1233,6 +1318,8 @@ var parseDurationTests = []struct { {"39h9m14.425s", true, 39*Hour + 9*Minute + 14*Second + 425*Millisecond}, // large value {"52763797000ns", true, 52763797000 * Nanosecond}, + // more than 9 digits after decimal point, see http://golang.org/issue/6617 + {"0.3333333333333333333h", true, 20 * Minute}, // errors {"", false, 0}, @@ -1300,6 +1387,9 @@ var mallocTest = []struct { } func TestCountMallocs(t *testing.T) { + if testing.Short() { + t.Skip("skipping malloc count in short mode") + } if runtime.GOMAXPROCS(0) > 1 { t.Skip("skipping; GOMAXPROCS>1") } @@ -1327,6 +1417,40 @@ func TestLoadFixed(t *testing.T) { } } +const ( + minDuration Duration = -1 << 63 + maxDuration Duration = 1<<63 - 1 +) + +var subTests = []struct { + t Time + u Time + d Duration +}{ + {Time{}, Time{}, Duration(0)}, + {Date(2009, 11, 23, 0, 0, 0, 1, UTC), Date(2009, 11, 23, 0, 0, 0, 0, UTC), Duration(1)}, + {Date(2009, 11, 23, 0, 0, 0, 0, UTC), Date(2009, 11, 24, 0, 0, 0, 0, UTC), -24 * Hour}, + {Date(2009, 11, 24, 0, 0, 0, 0, UTC), Date(2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour}, + {Date(-2009, 11, 24, 0, 0, 0, 0, UTC), Date(-2009, 11, 23, 0, 0, 0, 0, UTC), 24 * Hour}, + {Time{}, Date(2109, 11, 23, 0, 0, 0, 0, UTC), Duration(minDuration)}, + {Date(2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(maxDuration)}, + {Time{}, Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Duration(maxDuration)}, + {Date(-2109, 11, 23, 0, 0, 0, 0, UTC), Time{}, Duration(minDuration)}, + {Date(2290, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), 290*365*24*Hour + 71*24*Hour}, + {Date(2300, 1, 1, 0, 0, 0, 0, UTC), Date(2000, 1, 1, 0, 0, 0, 0, UTC), Duration(maxDuration)}, + {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2290, 1, 1, 0, 0, 0, 0, UTC), -290*365*24*Hour - 71*24*Hour}, + {Date(2000, 1, 1, 0, 0, 0, 0, UTC), Date(2300, 1, 1, 0, 0, 0, 0, UTC), Duration(minDuration)}, +} + +func TestSub(t *testing.T) { + for i, st := range subTests { + got := st.t.Sub(st.u) + if got != st.d { + t.Errorf("#%d: Sub(%v, %v): got %v; want %v", i, st.t, st.u, got, st.d) + } + } +} + func BenchmarkNow(b *testing.B) { for i := 0; i < b.N; i++ { t = Now() diff --git a/libgo/go/time/zoneinfo.go b/libgo/go/time/zoneinfo.go index c44477f..1c61862 100644 --- a/libgo/go/time/zoneinfo.go +++ b/libgo/go/time/zoneinfo.go @@ -178,19 +178,6 @@ func (l *Location) lookupName(name string, unix int64) (offset int, isDST bool, return } -// lookupOffset returns information about the time zone with -// the given offset (such as -5*60*60). -func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) { - l = l.get() - for i := range l.zone { - zone := &l.zone[i] - if zone.offset == offset { - return zone.name, zone.isDST, true - } - } - return -} - // NOTE(rsc): Eventually we will need to accept the POSIX TZ environment // syntax too, but I don't feel like implementing it today. diff --git a/libgo/go/time/zoneinfo_abbrs_windows.go b/libgo/go/time/zoneinfo_abbrs_windows.go new file mode 100644 index 0000000..8033437 --- /dev/null +++ b/libgo/go/time/zoneinfo_abbrs_windows.go @@ -0,0 +1,115 @@ +// 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. + +// generated by genzabbrs.go from +// http://unicode.org/cldr/data/common/supplemental/windowsZones.xml + +package time + +type abbr struct { + std string + dst string +} + +var abbrs = map[string]abbr{ + "Egypt Standard Time": {"EET", "EET"}, // Africa/Cairo + "Morocco Standard Time": {"WET", "WEST"}, // Africa/Casablanca + "South Africa Standard Time": {"SAST", "SAST"}, // Africa/Johannesburg + "W. Central Africa Standard Time": {"WAT", "WAT"}, // Africa/Lagos + "E. Africa Standard Time": {"EAT", "EAT"}, // Africa/Nairobi + "Namibia Standard Time": {"WAT", "WAST"}, // Africa/Windhoek + "Alaskan Standard Time": {"AKST", "AKDT"}, // America/Anchorage + "Paraguay Standard Time": {"PYT", "PYST"}, // America/Asuncion + "Bahia Standard Time": {"BRT", "BRST"}, // America/Bahia + "SA Pacific Standard Time": {"COT", "COT"}, // America/Bogota + "Argentina Standard Time": {"ART", "ART"}, // America/Buenos_Aires + "Venezuela Standard Time": {"VET", "VET"}, // America/Caracas + "SA Eastern Standard Time": {"GFT", "GFT"}, // America/Cayenne + "Central Standard Time": {"CST", "CDT"}, // America/Chicago + "Mountain Standard Time (Mexico)": {"MST", "MDT"}, // America/Chihuahua + "Central Brazilian Standard Time": {"AMT", "AMST"}, // America/Cuiaba + "Mountain Standard Time": {"MST", "MDT"}, // America/Denver + "Greenland Standard Time": {"WGT", "WGST"}, // America/Godthab + "Central America Standard Time": {"CST", "CST"}, // America/Guatemala + "Atlantic Standard Time": {"AST", "ADT"}, // America/Halifax + "US Eastern Standard Time": {"EST", "EDT"}, // America/Indianapolis + "SA Western Standard Time": {"BOT", "BOT"}, // America/La_Paz + "Pacific Standard Time": {"PST", "PDT"}, // America/Los_Angeles + "Central Standard Time (Mexico)": {"CST", "CDT"}, // America/Mexico_City + "Montevideo Standard Time": {"UYT", "UYST"}, // America/Montevideo + "Eastern Standard Time": {"EST", "EDT"}, // America/New_York + "US Mountain Standard Time": {"MST", "MST"}, // America/Phoenix + "Canada Central Standard Time": {"CST", "CST"}, // America/Regina + "Pacific Standard Time (Mexico)": {"PST", "PDT"}, // America/Santa_Isabel + "Pacific SA Standard Time": {"CLT", "CLST"}, // America/Santiago + "E. South America Standard Time": {"BRT", "BRST"}, // America/Sao_Paulo + "Newfoundland Standard Time": {"NST", "NDT"}, // America/St_Johns + "Central Asia Standard Time": {"ALMT", "ALMT"}, // Asia/Almaty + "Jordan Standard Time": {"EET", "EEST"}, // Asia/Amman + "Arabic Standard Time": {"AST", "AST"}, // Asia/Baghdad + "Azerbaijan Standard Time": {"AZT", "AZST"}, // Asia/Baku + "SE Asia Standard Time": {"ICT", "ICT"}, // Asia/Bangkok + "Middle East Standard Time": {"EET", "EEST"}, // Asia/Beirut + "India Standard Time": {"IST", "IST"}, // Asia/Calcutta + "Sri Lanka Standard Time": {"IST", "IST"}, // Asia/Colombo + "Syria Standard Time": {"EET", "EEST"}, // Asia/Damascus + "Bangladesh Standard Time": {"BDT", "BDT"}, // Asia/Dhaka + "Arabian Standard Time": {"GST", "GST"}, // Asia/Dubai + "North Asia East Standard Time": {"IRKT", "IRKT"}, // Asia/Irkutsk + "Israel Standard Time": {"IST", "IDT"}, // Asia/Jerusalem + "Afghanistan Standard Time": {"AFT", "AFT"}, // Asia/Kabul + "Pakistan Standard Time": {"PKT", "PKT"}, // Asia/Karachi + "Nepal Standard Time": {"NPT", "NPT"}, // Asia/Katmandu + "North Asia Standard Time": {"KRAT", "KRAT"}, // Asia/Krasnoyarsk + "Magadan Standard Time": {"MAGT", "MAGT"}, // Asia/Magadan + "E. Europe Standard Time": {"EET", "EEST"}, // Asia/Nicosia + "N. Central Asia Standard Time": {"NOVT", "NOVT"}, // Asia/Novosibirsk + "Myanmar Standard Time": {"MMT", "MMT"}, // Asia/Rangoon + "Arab Standard Time": {"AST", "AST"}, // Asia/Riyadh + "Korea Standard Time": {"KST", "KST"}, // Asia/Seoul + "China Standard Time": {"CST", "CST"}, // Asia/Shanghai + "Singapore Standard Time": {"SGT", "SGT"}, // Asia/Singapore + "Taipei Standard Time": {"CST", "CST"}, // Asia/Taipei + "West Asia Standard Time": {"UZT", "UZT"}, // Asia/Tashkent + "Georgian Standard Time": {"GET", "GET"}, // Asia/Tbilisi + "Iran Standard Time": {"IRST", "IRDT"}, // Asia/Tehran + "Tokyo Standard Time": {"JST", "JST"}, // Asia/Tokyo + "Ulaanbaatar Standard Time": {"ULAT", "ULAT"}, // Asia/Ulaanbaatar + "Vladivostok Standard Time": {"VLAT", "VLAT"}, // Asia/Vladivostok + "Yakutsk Standard Time": {"YAKT", "YAKT"}, // Asia/Yakutsk + "Ekaterinburg Standard Time": {"YEKT", "YEKT"}, // Asia/Yekaterinburg + "Caucasus Standard Time": {"AMT", "AMT"}, // Asia/Yerevan + "Azores Standard Time": {"AZOT", "AZOST"}, // Atlantic/Azores + "Cape Verde Standard Time": {"CVT", "CVT"}, // Atlantic/Cape_Verde + "Greenwich Standard Time": {"GMT", "GMT"}, // Atlantic/Reykjavik + "Cen. Australia Standard Time": {"CST", "CST"}, // Australia/Adelaide + "E. Australia Standard Time": {"EST", "EST"}, // Australia/Brisbane + "AUS Central Standard Time": {"CST", "CST"}, // Australia/Darwin + "Tasmania Standard Time": {"EST", "EST"}, // Australia/Hobart + "W. Australia Standard Time": {"WST", "WST"}, // Australia/Perth + "AUS Eastern Standard Time": {"EST", "EST"}, // Australia/Sydney + "UTC": {"GMT", "GMT"}, // Etc/GMT + "UTC-11": {"GMT+11", "GMT+11"}, // Etc/GMT+11 + "Dateline Standard Time": {"GMT+12", "GMT+12"}, // Etc/GMT+12 + "UTC-02": {"GMT+2", "GMT+2"}, // Etc/GMT+2 + "UTC+12": {"GMT-12", "GMT-12"}, // Etc/GMT-12 + "W. Europe Standard Time": {"CET", "CEST"}, // Europe/Berlin + "GTB Standard Time": {"EET", "EEST"}, // Europe/Bucharest + "Central Europe Standard Time": {"CET", "CEST"}, // Europe/Budapest + "Turkey Standard Time": {"EET", "EEST"}, // Europe/Istanbul + "Kaliningrad Standard Time": {"FET", "FET"}, // Europe/Kaliningrad + "FLE Standard Time": {"EET", "EEST"}, // Europe/Kiev + "GMT Standard Time": {"GMT", "BST"}, // Europe/London + "Russian Standard Time": {"MSK", "MSK"}, // Europe/Moscow + "Romance Standard Time": {"CET", "CEST"}, // Europe/Paris + "Central European Standard Time": {"CET", "CEST"}, // Europe/Warsaw + "Mauritius Standard Time": {"MUT", "MUT"}, // Indian/Mauritius + "Samoa Standard Time": {"WST", "WST"}, // Pacific/Apia + "New Zealand Standard Time": {"NZST", "NZDT"}, // Pacific/Auckland + "Fiji Standard Time": {"FJT", "FJT"}, // Pacific/Fiji + "Central Pacific Standard Time": {"SBT", "SBT"}, // Pacific/Guadalcanal + "Hawaiian Standard Time": {"HST", "HST"}, // Pacific/Honolulu + "West Pacific Standard Time": {"PGT", "PGT"}, // Pacific/Port_Moresby + "Tonga Standard Time": {"TOT", "TOT"}, // Pacific/Tongatapu +} diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go index 4519c99..7714aa9 100644 --- a/libgo/go/time/zoneinfo_read.go +++ b/libgo/go/time/zoneinfo_read.go @@ -11,10 +11,6 @@ package time import "errors" -const ( - headerSize = 4 + 16 + 4*7 -) - // Simple I/O interface to binary blob of data. type data struct { p []byte diff --git a/libgo/go/time/zoneinfo_unix.go b/libgo/go/time/zoneinfo_unix.go index 1bf1f11..7207025 100644 --- a/libgo/go/time/zoneinfo_unix.go +++ b/libgo/go/time/zoneinfo_unix.go @@ -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 freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd // Parse "zoneinfo" time zone file. // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others. diff --git a/libgo/go/time/zoneinfo_windows.go b/libgo/go/time/zoneinfo_windows.go index a8d3dcb..1e18ad2 100644 --- a/libgo/go/time/zoneinfo_windows.go +++ b/libgo/go/time/zoneinfo_windows.go @@ -8,6 +8,7 @@ import ( "errors" "runtime" "syscall" + "unsafe" ) // TODO(rsc): Fall back to copy of zoneinfo files. @@ -16,21 +17,83 @@ import ( // time zone information. // The implementation assumes that this year's rules for daylight savings // time apply to all previous and future years as well. -// Also, time zone abbreviations are unavailable. The implementation constructs -// them using the capital letters from a longer time zone description. - -// abbrev returns the abbreviation to use for the given zone name. -func abbrev(name []uint16) string { - // name is 'Pacific Standard Time' but we want 'PST'. - // Extract just capital letters. It's not perfect but the - // information we need is not available from the kernel. - // Because time zone abbreviations are not unique, - // Windows refuses to expose them. - // - // http://social.msdn.microsoft.com/Forums/eu/vclanguage/thread/a87e1d25-fb71-4fe0-ae9c-a9578c9753eb - // http://stackoverflow.com/questions/4195948/windows-time-zone-abbreviations-in-asp-net + +// getKeyValue retrieves the string value kname associated with the open registry key kh. +func getKeyValue(kh syscall.Handle, kname string) (string, error) { + var buf [50]uint16 // buf needs to be large enough to fit zone descriptions + var typ uint32 + n := uint32(len(buf) * 2) // RegQueryValueEx's signature expects array of bytes, not uint16 + p, _ := syscall.UTF16PtrFromString(kname) + if err := syscall.RegQueryValueEx(kh, p, nil, &typ, (*byte)(unsafe.Pointer(&buf[0])), &n); err != nil { + return "", err + } + if typ != syscall.REG_SZ { // null terminated strings only + return "", errors.New("Key is not string") + } + return syscall.UTF16ToString(buf[:]), nil +} + +// matchZoneKey checks if stdname and dstname match the corresponding "Std" +// and "Dlt" key values in the kname key stored under the open registry key zones. +func matchZoneKey(zones syscall.Handle, kname string, stdname, dstname string) (matched bool, err2 error) { + var h syscall.Handle + p, _ := syscall.UTF16PtrFromString(kname) + if err := syscall.RegOpenKeyEx(zones, p, 0, syscall.KEY_READ, &h); err != nil { + return false, err + } + defer syscall.RegCloseKey(h) + + s, err := getKeyValue(h, "Std") + if err != nil { + return false, err + } + if s != stdname { + return false, nil + } + s, err = getKeyValue(h, "Dlt") + if err != nil { + return false, err + } + if s != dstname { + return false, nil + } + return true, nil +} + +// toEnglishName searches the registry for an English name of a time zone +// whose zone names are stdname and dstname and returns the English name. +func toEnglishName(stdname, dstname string) (string, error) { + var zones syscall.Handle + p, _ := syscall.UTF16PtrFromString(`SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`) + if err := syscall.RegOpenKeyEx(syscall.HKEY_LOCAL_MACHINE, p, 0, syscall.KEY_READ, &zones); err != nil { + return "", err + } + defer syscall.RegCloseKey(zones) + + var count uint32 + if err := syscall.RegQueryInfoKey(zones, nil, nil, nil, &count, nil, nil, nil, nil, nil, nil, nil); err != nil { + return "", err + } + + var buf [50]uint16 // buf needs to be large enough to fit zone descriptions + for i := uint32(0); i < count; i++ { + n := uint32(len(buf)) + if syscall.RegEnumKeyEx(zones, i, &buf[0], &n, nil, nil, nil, nil) != nil { + continue + } + kname := syscall.UTF16ToString(buf[:]) + matched, err := matchZoneKey(zones, kname, stdname, dstname) + if err == nil && matched { + return kname, nil + } + } + return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`) +} + +// extractCAPS exracts capital letters from description desc. +func extractCAPS(desc string) string { var short []rune - for _, c := range name { + for _, c := range desc { if 'A' <= c && c <= 'Z' { short = append(short, rune(c)) } @@ -38,6 +101,26 @@ func abbrev(name []uint16) string { return string(short) } +// abbrev returns the abbreviations to use for the given zone z. +func abbrev(z *syscall.Timezoneinformation) (std, dst string) { + stdName := syscall.UTF16ToString(z.StandardName[:]) + a, ok := abbrs[stdName] + if !ok { + dstName := syscall.UTF16ToString(z.DaylightName[:]) + // Perhaps stdName is not English. Try to convert it. + englishName, err := toEnglishName(stdName, dstName) + if err == nil { + a, ok = abbrs[englishName] + if ok { + return a.std, a.dst + } + } + // fallback to using capital letters + return extractCAPS(stdName), extractCAPS(dstName) + } + return a.std, a.dst +} + // pseudoUnix returns the pseudo-Unix time (seconds since Jan 1 1970 *LOCAL TIME*) // denoted by the system date+time d in the given year. // It is up to the caller to convert this local time into a UTC-based time. @@ -75,8 +158,10 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) { } l.zone = make([]zone, nzone) + stdname, dstname := abbrev(i) + std := &l.zone[0] - std.name = abbrev(i.StandardName[0:]) + std.name = stdname if nzone == 1 { // No daylight savings. std.offset = -int(i.Bias) * 60 @@ -95,7 +180,7 @@ func initLocalFromTZI(i *syscall.Timezoneinformation) { std.offset = -int(i.Bias+i.StandardBias) * 60 dst := &l.zone[1] - dst.name = abbrev(i.DaylightName[0:]) + dst.name = dstname dst.offset = -int(i.Bias+i.DaylightBias) * 60 dst.isDST = true @@ -142,10 +227,27 @@ var usPacific = syscall.Timezoneinformation{ DaylightBias: -60, } +var aus = syscall.Timezoneinformation{ + Bias: -10 * 60, + StandardName: [32]uint16{ + 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'T', 'i', 'm', 'e', + }, + StandardDate: syscall.Systemtime{Month: 4, Day: 1, Hour: 3}, + DaylightName: [32]uint16{ + 'A', 'U', 'S', ' ', 'E', 'a', 's', 't', 'e', 'r', 'n', ' ', 'D', 'a', 'y', 'l', 'i', 'g', 'h', 't', ' ', 'T', 'i', 'm', 'e', + }, + DaylightDate: syscall.Systemtime{Month: 10, Day: 1, Hour: 2}, + DaylightBias: -60, +} + func initTestingZone() { initLocalFromTZI(&usPacific) } +func initAusTestingZone() { + initLocalFromTZI(&aus) +} + func initLocal() { var i syscall.Timezoneinformation if _, err := syscall.GetTimeZoneInformation(&i); err != nil { diff --git a/libgo/go/unicode/graphic.go b/libgo/go/unicode/graphic.go index 5b995fc..ba90b4e 100644 --- a/libgo/go/unicode/graphic.go +++ b/libgo/go/unicode/graphic.go @@ -39,7 +39,7 @@ func IsGraphic(r rune) bool { if uint32(r) <= MaxLatin1 { return properties[uint8(r)]&pg != 0 } - return IsOneOf(GraphicRanges, r) + return In(r, GraphicRanges...) } // IsPrint reports whether the rune is defined as printable by Go. Such @@ -51,12 +51,23 @@ func IsPrint(r rune) bool { if uint32(r) <= MaxLatin1 { return properties[uint8(r)]&pp != 0 } - return IsOneOf(PrintRanges, r) + return In(r, PrintRanges...) } // IsOneOf reports whether the rune is a member of one of the ranges. -func IsOneOf(set []*RangeTable, r rune) bool { - for _, inside := range set { +// The function "In" provides a nicer signature and should be used in preference to IsOneOf. +func IsOneOf(ranges []*RangeTable, r rune) bool { + for _, inside := range ranges { + if Is(inside, r) { + return true + } + } + return false +} + +// In reports whether the rune is a member of one of the ranges. +func In(r rune, ranges ...*RangeTable) bool { + for _, inside := range ranges { if Is(inside, r) { return true } diff --git a/libgo/go/unicode/graphic_test.go b/libgo/go/unicode/graphic_test.go index 7b1f620..c9f289c 100644 --- a/libgo/go/unicode/graphic_test.go +++ b/libgo/go/unicode/graphic_test.go @@ -71,7 +71,7 @@ func TestNumberLatin1(t *testing.T) { func TestIsPrintLatin1(t *testing.T) { for i := rune(0); i <= MaxLatin1; i++ { got := IsPrint(i) - want := IsOneOf(PrintRanges, i) + want := In(i, PrintRanges...) if i == ' ' { want = true } @@ -84,7 +84,7 @@ func TestIsPrintLatin1(t *testing.T) { func TestIsGraphicLatin1(t *testing.T) { for i := rune(0); i <= MaxLatin1; i++ { got := IsGraphic(i) - want := IsOneOf(GraphicRanges, i) + want := In(i, GraphicRanges...) if got != want { t.Errorf("%U incorrect: got %t; want %t", i, got, want) } diff --git a/libgo/mksysinfo.sh b/libgo/mksysinfo.sh index 71c3286..a1713df 100755 --- a/libgo/mksysinfo.sh +++ b/libgo/mksysinfo.sh @@ -160,6 +160,9 @@ cat > sysinfo.c <<EOF #if defined(HAVE_SYS_INOTIFY_H) #include <sys/inotify.h> #endif +#if defined(HAVE_NETINET_ICMP6_H) +#include <netinet/icmp6.h> +#endif /* Constants that may only be defined as expressions on some systems, expressions too complex for -fdump-go-spec to handle. These are @@ -709,6 +712,18 @@ if ! grep 'type IPMreqn ' ${OUT} >/dev/null 2>&1; then echo 'type IPMreqn struct { Multiaddr [4]byte; Interface [4]byte; Ifindex int32 }' >> ${OUT} fi +# The icmp6_filter struct. +grep '^type _icmp6_filter ' gen-sysinfo.go | \ + sed -e 's/_icmp6_filter/ICMPv6Filter/' \ + -e 's/data/Data/' \ + -e 's/filt/Filt/' \ + >> ${OUT} + +# We need ICMPv6Filter to compile the syscall package. +if ! grep 'type ICMPv6Filter ' ${OUT} > /dev/null 2>&1; then + echo 'type ICMPv6Filter struct { Data [8]uint32 }' >> ${OUT} +fi + # Try to guess the type to use for fd_set. fd_set=`grep '^type _fd_set ' gen-sysinfo.go || true` fds_bits_type="_C_long" @@ -1121,7 +1136,8 @@ set cmsghdr Cmsghdr ip_mreq IPMreq ip_mreqn IPMreqn ipv6_mreq IPv6Mreq \ in6_pktinfo Inet6Pktinfo inotify_event InotifyEvent linger Linger \ msghdr Msghdr nlattr NlAttr nlmsgerr NlMsgerr nlmsghdr NlMsghdr \ rtattr RtAttr rtgenmsg RtGenmsg rtmsg RtMsg rtnexthop RtNexthop \ - sock_filter SockFilter sock_fprog SockFprog ucred Ucred + sock_filter SockFilter sock_fprog SockFprog ucred Ucred \ + icmp6_filter ICMPv6Filter while test $# != 0; do nc=$1 ngo=$2 @@ -1143,5 +1159,8 @@ fi if ! grep 'const SizeofIPMreqn ' ${OUT} >/dev/null 2>&1; then echo 'const SizeofIPMreqn = 12' >> ${OUT} fi +if ! grep 'const SizeofICMPv6Filter ' ${OUT} >/dev/null 2>&1; then + echo 'const SizeofICMPv6Filter = 32' >> ${OUT} +fi exit $? diff --git a/libgo/runtime/chan.c b/libgo/runtime/chan.c index 6f52a1d..1d9e668 100644 --- a/libgo/runtime/chan.c +++ b/libgo/runtime/chan.c @@ -10,8 +10,6 @@ #define NOSELGEN 1 -static int32 debug = 0; - typedef struct WaitQ WaitQ; typedef struct SudoG SudoG; typedef struct Select Select; @@ -42,8 +40,9 @@ struct Hchan uintgo qcount; // total data in the q uintgo dataqsiz; // size of the circular q uint16 elemsize; - bool closed; uint8 elemalign; + uint8 pad; // ensures proper alignment of the buffer that follows Hchan in memory + bool closed; uintgo sendx; // send index uintgo recvx; // receive index WaitQ recvq; // list of recv waiters @@ -59,6 +58,8 @@ uint32 runtime_Hchansize = sizeof(Hchan); enum { + debug = 0, + // Scase.kind CaseRecv, CaseSend, @@ -105,17 +106,17 @@ runtime_makechan_c(ChanType *t, int64 hint) runtime_panicstring("makechan: size out of range"); n = sizeof(*c); + n = ROUND(n, elem->__align); // allocate memory in one call - c = (Hchan*)runtime_mal(n + hint*elem->__size); + c = (Hchan*)runtime_mallocgc(n + hint*elem->__size, (uintptr)t | TypeInfo_Chan, 0); c->elemsize = elem->__size; c->elemalign = elem->__align; c->dataqsiz = hint; - runtime_settype(c, (uintptr)t | TypeInfo_Chan); if(debug) - runtime_printf("makechan: chan=%p; elemsize=%D; elemalign=%d; dataqsiz=%D\n", - c, (int64)elem->__size, elem->__align, (int64)c->dataqsiz); + runtime_printf("makechan: chan=%p; elemsize=%D; dataqsiz=%D\n", + c, (int64)elem->__size, (int64)c->dataqsiz); return c; } @@ -185,7 +186,7 @@ runtime_chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc) return; // not reached } - if(runtime_gcwaiting) + if(runtime_gcwaiting()) runtime_gosched(); if(debug) { @@ -200,7 +201,6 @@ runtime_chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc) } runtime_lock(c); - // TODO(dvyukov): add similar instrumentation to select. if(raceenabled) runtime_racereadpc(c, pc, runtime_chansend); if(c->closed) @@ -311,7 +311,7 @@ runtime_chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *received int64 t0; G *g; - if(runtime_gcwaiting) + if(runtime_gcwaiting()) runtime_gosched(); if(debug) @@ -927,6 +927,7 @@ selectgo(Select **selp) { Select *sel; uint32 o, i, j, k; + int64 t0; Scase *cas, *dfl; Hchan *c; SudoG *sg; @@ -935,7 +936,7 @@ selectgo(Select **selp) G *g; sel = *selp; - if(runtime_gcwaiting) + if(runtime_gcwaiting()) runtime_gosched(); if(debug) @@ -943,6 +944,13 @@ selectgo(Select **selp) g = runtime_g(); + t0 = 0; + if(runtime_blockprofilerate > 0) { + t0 = runtime_cputicks(); + for(i=0; i<sel->ncase; i++) + sel->scase[i].sg.releasetime = -1; + } + // The compiler rewrites selects that statically have // only 0 or 1 cases plus default into simpler constructs. // The only way we can end up with such small sel->ncase @@ -1023,6 +1031,8 @@ loop: break; case CaseSend: + if(raceenabled) + runtime_racereadpc(c, runtime_selectgo, runtime_chansend); if(c->closed) goto sclose; if(c->dataqsiz > 0) { @@ -1124,6 +1134,8 @@ asyncrecv: if(sg != nil) { gp = sg->g; selunlock(sel); + if(sg->releasetime) + sg->releasetime = runtime_cputicks(); runtime_ready(gp); } else { selunlock(sel); @@ -1142,6 +1154,8 @@ asyncsend: if(sg != nil) { gp = sg->g; selunlock(sel); + if(sg->releasetime) + sg->releasetime = runtime_cputicks(); runtime_ready(gp); } else { selunlock(sel); @@ -1161,6 +1175,8 @@ syncrecv: 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; @@ -1186,11 +1202,15 @@ syncsend: 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); retc: // return index corresponding to chosen case index = cas->index; + if(cas->sg.releasetime > 0) + runtime_blockevent(cas->sg.releasetime - t0, 2); runtime_free(sel); return index; @@ -1297,17 +1317,36 @@ reflect_rselect(Slice cases) return ret; } +static void closechan(Hchan *c, void *pc); + // closechan(sel *byte); void runtime_closechan(Hchan *c) { + closechan(c, runtime_getcallerpc(&c)); +} + +// For reflect +// func chanclose(c chan) + +void reflect_chanclose(uintptr) __asm__ (GOSYM_PREFIX "reflect.chanclose"); + +void +reflect_chanclose(uintptr c) +{ + closechan((Hchan*)c, runtime_getcallerpc(&c)); +} + +static void +closechan(Hchan *c, void *pc) +{ SudoG *sg; G* gp; if(c == nil) runtime_panicstring("close of nil channel"); - if(runtime_gcwaiting) + if(runtime_gcwaiting()) runtime_gosched(); runtime_lock(c); @@ -1317,7 +1356,7 @@ runtime_closechan(Hchan *c) } if(raceenabled) { - runtime_racewritepc(c, runtime_getcallerpc(&c), runtime_closechan); + runtime_racewritepc(c, pc, runtime_closechan); runtime_racerelease(c); } @@ -1330,6 +1369,8 @@ runtime_closechan(Hchan *c) break; gp = sg->g; gp->param = nil; + if(sg->releasetime) + sg->releasetime = runtime_cputicks(); runtime_ready(gp); } @@ -1340,6 +1381,8 @@ runtime_closechan(Hchan *c) break; gp = sg->g; gp->param = nil; + if(sg->releasetime) + sg->releasetime = runtime_cputicks(); runtime_ready(gp); } @@ -1353,17 +1396,6 @@ __go_builtin_close(Hchan *c) } // For reflect -// func chanclose(c chan) - -void reflect_chanclose(uintptr) __asm__ (GOSYM_PREFIX "reflect.chanclose"); - -void -reflect_chanclose(uintptr c) -{ - runtime_closechan((Hchan*)c); -} - -// For reflect // func chanlen(c chan) (len int) intgo reflect_chanlen(uintptr) __asm__ (GOSYM_PREFIX "reflect.chanlen"); diff --git a/libgo/runtime/cpuprof.c b/libgo/runtime/cpuprof.c index 5163873..a2a1a05 100644 --- a/libgo/runtime/cpuprof.c +++ b/libgo/runtime/cpuprof.c @@ -146,7 +146,7 @@ runtime_SetCPUProfileRate(intgo hz) runtime_lock(&lk); if(hz > 0) { if(prof == nil) { - prof = runtime_SysAlloc(sizeof *prof); + prof = runtime_SysAlloc(sizeof *prof, &mstats.other_sys); if(prof == nil) { runtime_printf("runtime: cpu profiling cannot allocate memory\n"); runtime_unlock(&lk); @@ -340,7 +340,7 @@ getprofile(Profile *p) if(p->wholding) { // Release previous log to signal handling side. - // Loop because we are racing against setprofile(off). + // Loop because we are racing against SetCPUProfileRate(0). for(;;) { n = p->handoff; if(n == 0) { @@ -367,9 +367,7 @@ getprofile(Profile *p) return ret; // Wait for new log. - runtime_entersyscallblock(); - runtime_notesleep(&p->wait); - runtime_exitsyscall(); + runtime_notetsleepg(&p->wait, -1); runtime_noteclear(&p->wait); n = p->handoff; diff --git a/libgo/runtime/env_posix.c b/libgo/runtime/env_posix.c index 7f3fa0d..3219550 100644 --- a/libgo/runtime/env_posix.c +++ b/libgo/runtime/env_posix.c @@ -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 freebsd linux netbsd openbsd windows +// +build darwin dragonfly freebsd linux netbsd openbsd windows #include "runtime.h" #include "array.h" @@ -12,7 +12,8 @@ extern Slice syscall_Envs __asm__ (GOSYM_PREFIX "syscall.Envs"); const byte* runtime_getenv(const char *s) { - int32 i, j, len; + int32 i, j; + intgo len; const byte *v, *bs; String* envv; int32 envc; diff --git a/libgo/runtime/go-byte-array-to-string.c b/libgo/runtime/go-byte-array-to-string.c index 0cd63c7..088b786 100644 --- a/libgo/runtime/go-byte-array-to-string.c +++ b/libgo/runtime/go-byte-array-to-string.c @@ -16,7 +16,7 @@ __go_byte_array_to_string (const void* p, intgo len) String ret; bytes = (const unsigned char *) p; - retdata = runtime_mallocgc ((uintptr) len, FlagNoPointers, 1, 0); + retdata = runtime_mallocgc ((uintptr) len, 0, FlagNoScan); __builtin_memcpy (retdata, bytes, len); ret.str = retdata; ret.len = len; diff --git a/libgo/runtime/go-caller.c b/libgo/runtime/go-caller.c index d84580f..8ca3c7e 100644 --- a/libgo/runtime/go-caller.c +++ b/libgo/runtime/go-caller.c @@ -228,3 +228,23 @@ runtime_funcline_go (Func *f __attribute__((unused)), uintptr targetpc) runtime_memclr (&ret, sizeof ret); return ret; } + +/* Return the name of a function. */ +String runtime_funcname_go (Func *f) + __asm__ (GOSYM_PREFIX "runtime.funcname_go"); + +String +runtime_funcname_go (Func *f) +{ + return f->name; +} + +/* Return the entry point of a function. */ +uintptr runtime_funcentry_go(Func *f) + __asm__ (GOSYM_PREFIX "runtime.funcentry_go"); + +uintptr +runtime_funcentry_go (Func *f) +{ + return f->entry; +} diff --git a/libgo/runtime/go-int-array-to-string.c b/libgo/runtime/go-int-array-to-string.c index 6cae2fd..d93fe65 100644 --- a/libgo/runtime/go-int-array-to-string.c +++ b/libgo/runtime/go-int-array-to-string.c @@ -41,7 +41,7 @@ __go_int_array_to_string (const void* p, intgo len) slen += 4; } - retdata = runtime_mallocgc ((uintptr) slen, FlagNoPointers, 1, 0); + retdata = runtime_mallocgc ((uintptr) slen, 0, FlagNoScan); ret.str = retdata; ret.len = slen; diff --git a/libgo/runtime/go-int-to-string.c b/libgo/runtime/go-int-to-string.c index eb44167..d90b1dd 100644 --- a/libgo/runtime/go-int-to-string.c +++ b/libgo/runtime/go-int-to-string.c @@ -60,7 +60,7 @@ __go_int_to_string (intgo v) } } - retdata = runtime_mallocgc (len, FlagNoPointers, 1, 0); + retdata = runtime_mallocgc (len, 0, FlagNoScan); __builtin_memcpy (retdata, buf, len); ret.str = retdata; ret.len = len; diff --git a/libgo/runtime/go-make-slice.c b/libgo/runtime/go-make-slice.c index f08cb01..855bb17 100644 --- a/libgo/runtime/go-make-slice.c +++ b/libgo/runtime/go-make-slice.c @@ -55,15 +55,15 @@ __go_make_slice2 (const struct __go_type_descriptor *td, uintptr_t len, if (size == 0) ret.__values = &runtime_zerobase; else if ((std->__element_type->__code & GO_NO_POINTERS) != 0) - ret.__values = runtime_mallocgc (size, FlagNoPointers, 1, 1); + ret.__values = + runtime_mallocgc (size, + (uintptr) std->__element_type | TypeInfo_Array, + FlagNoScan); else - { - ret.__values = runtime_mallocgc (size, 0, 1, 1); - - if (UseSpanType) - runtime_settype (ret.__values, - (uintptr) std->__element_type | TypeInfo_Array); - } + ret.__values = + runtime_mallocgc (size, + (uintptr) std->__element_type | TypeInfo_Array, + 0); return ret; } diff --git a/libgo/runtime/go-new.c b/libgo/runtime/go-new.c index b1af5f2..9d46706 100644 --- a/libgo/runtime/go-new.c +++ b/libgo/runtime/go-new.c @@ -12,11 +12,11 @@ void * __go_new (uintptr_t size) { - return runtime_mallocgc (size, 0, 1, 1); + return runtime_mallocgc (size, 0, 0); } void * __go_new_nopointers (uintptr_t size) { - return runtime_mallocgc (size, FlagNoPointers, 1, 1); + return runtime_mallocgc (size, 0, FlagNoScan); } diff --git a/libgo/runtime/go-reflect-call.c b/libgo/runtime/go-reflect-call.c index 5cf3707..0fed68a 100644 --- a/libgo/runtime/go-reflect-call.c +++ b/libgo/runtime/go-reflect-call.c @@ -271,7 +271,21 @@ go_func_return_ffi (const struct __go_func_type *func) types = (const struct __go_type_descriptor **) func->__out.__values; if (count == 1) - return go_type_to_ffi (types[0]); + { + +#if defined (__i386__) && !defined (__x86_64__) + /* FFI does not support complex types. On 32-bit x86, a + complex64 will be returned in %eax/%edx. We normally tell + FFI that a complex64 is a struct of two floats. On 32-bit + x86 a struct of two floats is returned via a hidden first + pointer parameter. Fortunately we can make everything work + by pretending that complex64 is int64. */ + if ((types[0]->__code & GO_CODE_MASK) == GO_COMPLEX64) + return &ffi_type_sint64; +#endif + + return go_type_to_ffi (types[0]); + } ret = (ffi_type *) __go_alloc (sizeof (ffi_type)); ret->type = FFI_TYPE_STRUCT; diff --git a/libgo/runtime/go-signal.c b/libgo/runtime/go-signal.c index 23a94db..4f0dcc7 100644 --- a/libgo/runtime/go-signal.c +++ b/libgo/runtime/go-signal.c @@ -139,22 +139,6 @@ SigTab runtime_sigtab[] = { #undef P #undef D - -static int8 badsignal[] = "runtime: signal received on thread not created by Go.\n"; - -static void -runtime_badsignal(int32 sig) -{ - // Avoid -D_FORTIFY_SOURCE problems. - int rv __attribute__((unused)); - - if (sig == SIGPROF) { - return; // Ignore SIGPROFs intended for a non-Go thread. - } - rv = runtime_write(2, badsignal, sizeof badsignal - 1); - runtime_exit(1); -} - /* Handle a signal, for cases where we don't panic. We can split the stack here. */ diff --git a/libgo/runtime/go-string-to-byte-array.c b/libgo/runtime/go-string-to-byte-array.c index 75fac1d..5e03033 100644 --- a/libgo/runtime/go-string-to-byte-array.c +++ b/libgo/runtime/go-string-to-byte-array.c @@ -15,7 +15,8 @@ __go_string_to_byte_array (String str) unsigned char *data; struct __go_open_array ret; - data = (unsigned char *) runtime_mallocgc (str.len, FlagNoPointers, 1, 0); + data = (unsigned char *) runtime_mallocgc (str.len, 0, + FlagNoScan | FlagNoZero); __builtin_memcpy (data, str.str, str.len); ret.__values = (void *) data; ret.__count = str.len; diff --git a/libgo/runtime/go-string-to-int-array.c b/libgo/runtime/go-string-to-int-array.c index 16970bd..d91c9e2 100644 --- a/libgo/runtime/go-string-to-int-array.c +++ b/libgo/runtime/go-string-to-int-array.c @@ -32,8 +32,8 @@ __go_string_to_int_array (String str) p += __go_get_rune (p, pend - p, &rune); } - data = (uint32_t *) runtime_mallocgc (c * sizeof (uint32_t), FlagNoPointers, - 1, 0); + data = (uint32_t *) runtime_mallocgc (c * sizeof (uint32_t), 0, + FlagNoScan | FlagNoZero); p = str.str; pd = data; while (p < pend) diff --git a/libgo/runtime/go-strplus.c b/libgo/runtime/go-strplus.c index d6e6df6..13915e3 100644 --- a/libgo/runtime/go-strplus.c +++ b/libgo/runtime/go-strplus.c @@ -21,7 +21,7 @@ __go_string_plus (String s1, String s2) return s1; len = s1.len + s2.len; - retdata = runtime_mallocgc (len, FlagNoPointers, 1, 0); + retdata = runtime_mallocgc (len, 0, FlagNoScan | FlagNoZero); __builtin_memcpy (retdata, s1.str, s1.len); __builtin_memcpy (retdata + s1.len, s2.str, s2.len); ret.str = retdata; diff --git a/libgo/runtime/lfstack.c b/libgo/runtime/lfstack.c index 230ed87..132783c 100644 --- a/libgo/runtime/lfstack.c +++ b/libgo/runtime/lfstack.c @@ -41,10 +41,10 @@ runtime_lfstackpush(uint64 *head, LFNode *node) node->pushcnt++; new = (uint64)(uintptr)node|(((uint64)node->pushcnt&CNT_MASK)<<PTR_BITS); - old = runtime_atomicload64(head); for(;;) { + old = runtime_atomicload64(head); node->next = (LFNode*)(uintptr)(old&PTR_MASK); - if(runtime_cas64(head, &old, new)) + if(runtime_cas64(head, old, new)) break; } } @@ -55,8 +55,8 @@ runtime_lfstackpop(uint64 *head) LFNode *node, *node2; uint64 old, new; - old = runtime_atomicload64(head); for(;;) { + old = runtime_atomicload64(head); if(old == 0) return nil; node = (LFNode*)(uintptr)(old&PTR_MASK); @@ -64,7 +64,7 @@ runtime_lfstackpop(uint64 *head) new = 0; if(node2 != nil) new = (uint64)(uintptr)node2|(((uint64)node2->pushcnt&CNT_MASK)<<PTR_BITS); - if(runtime_cas64(head, &old, new)) + if(runtime_cas64(head, old, new)) return node; } } diff --git a/libgo/runtime/lock_futex.c b/libgo/runtime/lock_futex.c index 4b9651a..fa27013 100644 --- a/libgo/runtime/lock_futex.c +++ b/libgo/runtime/lock_futex.c @@ -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 freebsd linux +// +build dragonfly freebsd linux #include "runtime.h" @@ -91,14 +91,14 @@ runtime_unlock(Lock *l) { uint32 v; - if(--runtime_m()->locks < 0) - runtime_throw("runtime_unlock: lock count"); - v = runtime_xchg((uint32*)&l->key, MUTEX_UNLOCKED); if(v == MUTEX_UNLOCKED) runtime_throw("unlock of unlocked lock"); if(v == MUTEX_SLEEPING) runtime_futexwakeup((uint32*)&l->key, 1); + + if(--runtime_m()->locks < 0) + runtime_throw("runtime_unlock: lock count"); } // One-time notifications. @@ -111,37 +111,45 @@ runtime_noteclear(Note *n) void runtime_notewakeup(Note *n) { - if(runtime_xchg((uint32*)&n->key, 1)) + uint32 old; + + old = runtime_xchg((uint32*)&n->key, 1); + if(old != 0) { + runtime_printf("notewakeup - double wakeup (%d)\n", old); runtime_throw("notewakeup - double wakeup"); + } runtime_futexwakeup((uint32*)&n->key, 1); } void runtime_notesleep(Note *n) { - if(runtime_m()->profilehz > 0) - runtime_setprof(false); + /* For gccgo it's OK to sleep in non-g0, and it happens in + stoptheworld because we have not implemented preemption. + + if(runtime_g() != runtime_m()->g0) + runtime_throw("notesleep not on g0"); + */ while(runtime_atomicload((uint32*)&n->key) == 0) runtime_futexsleep((uint32*)&n->key, 0, -1); - if(runtime_m()->profilehz > 0) - runtime_setprof(true); } -void -runtime_notetsleep(Note *n, int64 ns) +static bool +notetsleep(Note *n, int64 ns, int64 deadline, int64 now) { - int64 deadline, now; + // Conceptually, deadline and now are local variables. + // They are passed as arguments so that the space for them + // does not count against our nosplit stack sequence. if(ns < 0) { - runtime_notesleep(n); - return; + while(runtime_atomicload((uint32*)&n->key) == 0) + runtime_futexsleep((uint32*)&n->key, 0, -1); + return true; } if(runtime_atomicload((uint32*)&n->key) != 0) - return; + return true; - if(runtime_m()->profilehz > 0) - runtime_setprof(false); deadline = runtime_nanotime() + ns; for(;;) { runtime_futexsleep((uint32*)&n->key, 0, ns); @@ -152,6 +160,33 @@ runtime_notetsleep(Note *n, int64 ns) break; ns = deadline - now; } - if(runtime_m()->profilehz > 0) - runtime_setprof(true); + return runtime_atomicload((uint32*)&n->key) != 0; +} + +bool +runtime_notetsleep(Note *n, int64 ns) +{ + bool res; + + if(runtime_g() != runtime_m()->g0 && !runtime_m()->gcing) + runtime_throw("notetsleep not on g0"); + + res = notetsleep(n, ns, 0, 0); + return res; +} + +// same as runtime_notetsleep, but called on user g (not g0) +// calls only nosplit functions between entersyscallblock/exitsyscall +bool +runtime_notetsleepg(Note *n, int64 ns) +{ + bool res; + + if(runtime_g() == runtime_m()->g0) + runtime_throw("notetsleepg on g0"); + + runtime_entersyscallblock(); + res = notetsleep(n, ns, 0, 0); + runtime_exitsyscall(); + return res; } diff --git a/libgo/runtime/lock_sema.c b/libgo/runtime/lock_sema.c index 2663c54..ce43511 100644 --- a/libgo/runtime/lock_sema.c +++ b/libgo/runtime/lock_sema.c @@ -95,9 +95,6 @@ runtime_unlock(Lock *l) uintptr v; M *mp; - if(--runtime_m()->locks < 0) - runtime_throw("runtime_unlock: lock count"); - for(;;) { v = (uintptr)runtime_atomicloadp((void**)&l->key); if(v == LOCKED) { @@ -114,6 +111,9 @@ runtime_unlock(Lock *l) } } } + + if(--runtime_m()->locks < 0) + runtime_throw("runtime_unlock: lock count"); } // One-time notifications. @@ -151,6 +151,10 @@ runtime_notesleep(Note *n) M *m; m = runtime_m(); + + if(runtime_g() != m->g0) + runtime_throw("notesleep not on g0"); + if(m->waitsema == 0) m->waitsema = runtime_semacreate(); if(!runtime_casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup) @@ -159,61 +163,49 @@ runtime_notesleep(Note *n) return; } // Queued. Sleep. - if(m->profilehz > 0) - runtime_setprof(false); runtime_semasleep(-1); - if(m->profilehz > 0) - runtime_setprof(true); } -void -runtime_notetsleep(Note *n, int64 ns) +static bool +notetsleep(Note *n, int64 ns, int64 deadline, M *mp) { M *m; - M *mp; - int64 deadline, now; - - if(ns < 0) { - runtime_notesleep(n); - return; - } m = runtime_m(); - if(m->waitsema == 0) - m->waitsema = runtime_semacreate(); + + // Conceptually, deadline and mp are local variables. + // They are passed as arguments so that the space for them + // does not count against our nosplit stack sequence. // Register for wakeup on n->waitm. if(!runtime_casp((void**)&n->key, nil, m)) { // must be LOCKED (got wakeup already) if(n->key != LOCKED) runtime_throw("notetsleep - waitm out of sync"); - return; + return true; + } + + if(ns < 0) { + // Queued. Sleep. + runtime_semasleep(-1); + return true; } - if(m->profilehz > 0) - runtime_setprof(false); deadline = runtime_nanotime() + ns; for(;;) { // Registered. Sleep. if(runtime_semasleep(ns) >= 0) { // Acquired semaphore, semawakeup unregistered us. // Done. - if(m->profilehz > 0) - runtime_setprof(true); - return; + return true; } // Interrupted or timed out. Still registered. Semaphore not acquired. - now = runtime_nanotime(); - if(now >= deadline) + ns = deadline - runtime_nanotime(); + if(ns <= 0) break; - // Deadline hasn't arrived. Keep sleeping. - ns = deadline - now; } - if(m->profilehz > 0) - runtime_setprof(true); - // Deadline arrived. Still registered. Semaphore not acquired. // Want to give up and return, but have to unregister first, // so that any notewakeup racing with the return does not @@ -223,15 +215,54 @@ runtime_notetsleep(Note *n, int64 ns) if(mp == m) { // No wakeup yet; unregister if possible. if(runtime_casp((void**)&n->key, mp, nil)) - return; + return false; } else if(mp == (M*)LOCKED) { // Wakeup happened so semaphore is available. // Grab it to avoid getting out of sync. if(runtime_semasleep(-1) < 0) runtime_throw("runtime: unable to acquire - semaphore out of sync"); - return; - } else { + return true; + } else runtime_throw("runtime: unexpected waitm - semaphore out of sync"); - } } } + +bool +runtime_notetsleep(Note *n, int64 ns) +{ + M *m; + bool res; + + m = runtime_m(); + + if(runtime_g() != m->g0 && !m->gcing) + runtime_throw("notetsleep not on g0"); + + if(m->waitsema == 0) + m->waitsema = runtime_semacreate(); + + res = notetsleep(n, ns, 0, nil); + return res; +} + +// same as runtime_notetsleep, but called on user g (not g0) +// calls only nosplit functions between entersyscallblock/exitsyscall +bool +runtime_notetsleepg(Note *n, int64 ns) +{ + M *m; + bool res; + + m = runtime_m(); + + if(runtime_g() == m->g0) + runtime_throw("notetsleepg on g0"); + + if(m->waitsema == 0) + m->waitsema = runtime_semacreate(); + + runtime_entersyscallblock(); + res = notetsleep(n, ns, 0, nil); + runtime_exitsyscall(); + return res; +} diff --git a/libgo/runtime/malloc.goc b/libgo/runtime/malloc.goc index 8ccaa6b..d349f47 100644 --- a/libgo/runtime/malloc.goc +++ b/libgo/runtime/malloc.goc @@ -18,7 +18,17 @@ package runtime #include "go-type.h" #include "race.h" -MHeap *runtime_mheap; +// Map gccgo field names to gc field names. +// Eface aka __go_empty_interface. +#define type __type_descriptor +// Type aka __go_type_descriptor +#define kind __code +#define string __reflection +#define KindPtr GO_PTR +#define KindNoPointers GO_NO_POINTERS + +// Mark mheap as 'no pointers', it does not contain interesting pointers but occupies ~45K. +MHeap runtime_mheap; int32 runtime_checking; @@ -30,19 +40,28 @@ extern volatile intgo runtime_MemProfileRate // Allocate an object of at least size bytes. // Small objects are allocated from the per-thread cache's free lists. // Large objects (> 32 kB) are allocated straight from the heap. +// If the block will be freed with runtime_free(), typ must be 0. void* -runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) +runtime_mallocgc(uintptr size, uintptr typ, uint32 flag) { M *m; G *g; int32 sizeclass; intgo rate; MCache *c; + MCacheList *l; uintptr npages; MSpan *s; - void *v; + MLink *v; bool incallback; + if(size == 0) { + // All 0-length allocations use this pointer. + // The language does not require the allocations to + // have distinct values. + return &runtime_zerobase; + } + m = runtime_m(); g = runtime_g(); @@ -56,34 +75,45 @@ runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) runtime_exitsyscall(); m = runtime_m(); incallback = true; - dogc = false; + flag |= FlagNoGC; } - if(runtime_gcwaiting && g != m->g0 && m->locks == 0 && dogc) { + if(runtime_gcwaiting() && g != m->g0 && m->locks == 0 && !(flag & FlagNoGC)) { runtime_gosched(); m = runtime_m(); } 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. + m->locks++; m->mallocing = 1; - if(size == 0) - size = 1; if(DebugTypeAtBlockEnd) size += sizeof(uintptr); c = m->mcache; - c->local_nmalloc++; if(size <= MaxSmallSize) { // Allocate from mcache free lists. - sizeclass = runtime_SizeToClass(size); + // Inlined version of SizeToClass(). + if(size <= 1024-8) + sizeclass = runtime_size_to_class8[(size+7)>>3]; + else + sizeclass = runtime_size_to_class128[(size-1024+127) >> 7]; size = runtime_class_to_size[sizeclass]; - v = runtime_MCache_Alloc(c, sizeclass, size, zeroed); - if(v == nil) - runtime_throw("out of memory"); - c->local_alloc += size; - c->local_total_alloc += size; - c->local_by_size[sizeclass].nmalloc++; + l = &c->list[sizeclass]; + if(l->list == nil) + runtime_MCache_Refill(c, sizeclass); + v = l->list; + l->list = v->next; + l->nlist--; + if(!(flag & FlagNoZero)) { + v->next = nil; + // block is zeroed iff second word is zero ... + if(size > sizeof(uintptr) && ((uintptr*)v)[1] != 0) + runtime_memclr((byte*)v, size); + } + c->local_cachealloc += size; } else { // TODO(rsc): Report tracebacks for very large allocations. @@ -91,32 +121,39 @@ runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) npages = size >> PageShift; if((size & PageMask) != 0) npages++; - s = runtime_MHeap_Alloc(runtime_mheap, npages, 0, 1, zeroed); + s = runtime_MHeap_Alloc(&runtime_mheap, npages, 0, 1, !(flag & FlagNoZero)); if(s == nil) runtime_throw("out of memory"); + s->limit = (byte*)(s->start<<PageShift) + size; size = npages<<PageShift; - c->local_alloc += size; - c->local_total_alloc += size; v = (void*)(s->start << PageShift); // setup for mark sweep runtime_markspan(v, 0, 0, true); } - if (sizeof(void*) == 4 && c->local_total_alloc >= (1<<30)) { - // purge cache stats to prevent overflow - runtime_lock(runtime_mheap); - runtime_purgecachedstats(c); - runtime_unlock(runtime_mheap); - } - if(!(flag & FlagNoGC)) - runtime_markallocated(v, size, (flag&FlagNoPointers) != 0); + runtime_markallocated(v, size, (flag&FlagNoScan) != 0); if(DebugTypeAtBlockEnd) - *(uintptr*)((uintptr)v+size-sizeof(uintptr)) = 0; + *(uintptr*)((uintptr)v+size-sizeof(uintptr)) = typ; + + // 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; + } m->mallocing = 0; + if(UseSpanType && !(flag & FlagNoScan) && typ != 0 && m->settype_bufsize == nelem(m->settype_buf)) + runtime_settype_flush(m); + m->locks--; if(!(flag & FlagNoProfiling) && (rate = runtime_MemProfileRate) > 0) { if(size >= (uint32) rate) @@ -135,13 +172,11 @@ runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) } } - if(dogc && mstats.heap_alloc >= mstats.next_gc) + if(!(flag & FlagNoInvokeGC) && mstats.heap_alloc >= mstats.next_gc) runtime_gc(0); - if(raceenabled) { - runtime_racemalloc(v, size, m->racepc); - m->racepc = nil; - } + if(raceenabled) + runtime_racemalloc(v, size); if(incallback) runtime_entersyscall(); @@ -152,7 +187,7 @@ runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) void* __go_alloc(uintptr size) { - return runtime_mallocgc(size, 0, 0, 1); + return runtime_mallocgc(size, 0, FlagNoInvokeGC); } // Free the object whose base pointer is v. @@ -197,7 +232,9 @@ __go_free(void *v) // they might coalesce v into other spans and change the bitmap further. runtime_markfreed(v, size); runtime_unmarkspan(v, 1<<PageShift); - runtime_MHeap_Free(runtime_mheap, s, 1); + runtime_MHeap_Free(&runtime_mheap, s, 1); + c->local_nlargefree++; + c->local_largefree += size; } else { // Small object. size = runtime_class_to_size[sizeclass]; @@ -207,11 +244,9 @@ __go_free(void *v) // it might coalesce v and other blocks into a bigger span // and change the bitmap further. runtime_markfreed(v, size); - c->local_by_size[sizeclass].nfree++; + c->local_nsmallfree[sizeclass]++; runtime_MCache_Free(c, v, sizeclass, size); } - c->local_nfree++; - c->local_alloc -= size; if(prof) runtime_MProf_Free(v, size); m->mallocing = 0; @@ -230,12 +265,12 @@ runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp) m->mcache->local_nlookup++; if (sizeof(void*) == 4 && m->mcache->local_nlookup >= (1<<30)) { // purge cache stats to prevent overflow - runtime_lock(runtime_mheap); + runtime_lock(&runtime_mheap); runtime_purgecachedstats(m->mcache); - runtime_unlock(runtime_mheap); + runtime_unlock(&runtime_mheap); } - s = runtime_MHeap_LookupMaybe(runtime_mheap, v); + s = runtime_MHeap_LookupMaybe(&runtime_mheap, v); if(sp) *sp = s; if(s == nil) { @@ -257,11 +292,6 @@ runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp) return 1; } - if((byte*)v >= (byte*)s->limit) { - // pointers past the last block do not count as pointers. - return 0; - } - n = s->elemsize; if(base) { i = ((byte*)v - p)/n; @@ -279,11 +309,9 @@ runtime_allocmcache(void) intgo rate; MCache *c; - runtime_lock(runtime_mheap); - c = runtime_FixAlloc_Alloc(&runtime_mheap->cachealloc); - mstats.mcache_inuse = runtime_mheap->cachealloc.inuse; - mstats.mcache_sys = runtime_mheap->cachealloc.sys; - runtime_unlock(runtime_mheap); + 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. @@ -300,30 +328,32 @@ void runtime_freemcache(MCache *c) { runtime_MCache_ReleaseAll(c); - runtime_lock(runtime_mheap); + runtime_lock(&runtime_mheap); runtime_purgecachedstats(c); - runtime_FixAlloc_Free(&runtime_mheap->cachealloc, c); - runtime_unlock(runtime_mheap); + runtime_FixAlloc_Free(&runtime_mheap.cachealloc, c); + runtime_unlock(&runtime_mheap); } void runtime_purgecachedstats(MCache *c) { + MHeap *h; + int32 i; + // Protected by either heap or GC lock. + h = &runtime_mheap; mstats.heap_alloc += c->local_cachealloc; c->local_cachealloc = 0; - mstats.heap_objects += c->local_objects; - c->local_objects = 0; - mstats.nmalloc += c->local_nmalloc; - c->local_nmalloc = 0; - mstats.nfree += c->local_nfree; - c->local_nfree = 0; mstats.nlookup += c->local_nlookup; c->local_nlookup = 0; - mstats.alloc += c->local_alloc; - c->local_alloc= 0; - mstats.total_alloc += c->local_total_alloc; - c->local_total_alloc= 0; + h->largefree += c->local_largefree; + c->local_largefree = 0; + h->nlargefree += c->local_nlargefree; + c->local_nlargefree = 0; + for(i=0; i<(int32)nelem(c->local_nsmallfree); i++) { + h->nsmallfree[i] += c->local_nsmallfree[i]; + c->local_nsmallfree[i] = 0; + } } extern uintptr runtime_sizeof_C_MStats @@ -335,24 +365,24 @@ void runtime_mallocinit(void) { byte *p; - uintptr arena_size, bitmap_size; + uintptr arena_size, bitmap_size, spans_size; extern byte _end[]; byte *want; uintptr limit; + uint64 i; runtime_sizeof_C_MStats = sizeof(MStats); p = nil; arena_size = 0; bitmap_size = 0; - + spans_size = 0; + // for 64-bit build USED(p); USED(arena_size); USED(bitmap_size); - - if((runtime_mheap = runtime_SysAlloc(sizeof(*runtime_mheap))) == nil) - runtime_throw("runtime: cannot allocate heap metadata"); + USED(spans_size); runtime_InitSizes(); @@ -369,15 +399,17 @@ runtime_mallocinit(void) // 128 GB (MaxMem) should be big enough for now. // // The code will work with the reservation at any address, but ask - // SysReserve to use 0x000000c000000000 if possible. + // SysReserve to use 0x0000XXc000000000 if possible (XX=00...7f). // Allocating a 128 GB region takes away 37 bits, and the amd64 // doesn't let us choose the top 17 bits, so that leaves the 11 bits // in the middle of 0x00c0 for us to choose. Choosing 0x00c0 means - // that the valid memory addresses will begin 0x00c0, 0x00c1, ..., 0x0x00df. + // that the valid memory addresses will begin 0x00c0, 0x00c1, ..., 0x00df. // In little-endian, that's c0 00, c1 00, ..., df 00. None of those are valid // UTF-8 sequences, and they are otherwise as far away from - // ff (likely a common byte) as possible. An earlier attempt to use 0x11f8 - // caused out of memory errors on OS X during thread allocations. + // ff (likely a common byte) as possible. If that fails, we try other 0xXXc0 + // addresses. An earlier attempt to use 0x11f8 caused out of memory errors + // on OS X during thread allocations. 0x00c0 causes conflicts with + // AddressSanitizer which reserves all memory up to 0x0100. // These choices are both for debuggability and to reduce the // odds of the conservative garbage collector not collecting memory // because some non-pointer block of memory had a bit pattern @@ -389,7 +421,14 @@ runtime_mallocinit(void) // If this fails we fall back to the 32 bit memory mechanism arena_size = MaxMem; bitmap_size = arena_size / (sizeof(void*)*8/4); - p = runtime_SysReserve((void*)(0x00c0ULL<<32), bitmap_size + arena_size); + spans_size = arena_size / PageSize * sizeof(runtime_mheap.spans[0]); + spans_size = ROUND(spans_size, PageSize); + for(i = 0; i <= 0x7f; i++) { + p = (void*)(uintptr)(i<<40 | 0x00c0ULL<<32); + p = runtime_SysReserve(p, bitmap_size + spans_size + arena_size); + if(p != nil) + break; + } } if (p == nil) { // On a 32-bit machine, we can't typically get away @@ -411,11 +450,14 @@ runtime_mallocinit(void) // of address space, which is probably too much in a 32-bit world. bitmap_size = MaxArena32 / (sizeof(void*)*8/4); arena_size = 512<<20; - if(limit > 0 && arena_size+bitmap_size > limit) { + spans_size = MaxArena32 / PageSize * sizeof(runtime_mheap.spans[0]); + if(limit > 0 && arena_size+bitmap_size+spans_size > limit) { bitmap_size = (limit / 9) & ~((1<<PageShift) - 1); arena_size = bitmap_size * 8; + spans_size = arena_size / PageSize * sizeof(runtime_mheap.spans[0]); } - + spans_size = ROUND(spans_size, PageSize); + // SysReserve treats the address we ask for, end, as a hint, // not as an absolute requirement. If we ask for the end // of the data segment but the operating system requires @@ -425,25 +467,27 @@ runtime_mallocinit(void) // So adjust it upward a little bit ourselves: 1/4 MB to get // away from the running binary image and then round up // to a MB boundary. - want = (byte*)(((uintptr)_end + (1<<18) + (1<<20) - 1)&~((1<<20)-1)); - if(0xffffffff - (uintptr)want <= bitmap_size + arena_size) + want = (byte*)ROUND((uintptr)_end + (1<<18), 1<<20); + if(0xffffffff - (uintptr)want <= bitmap_size + spans_size + arena_size) want = 0; - p = runtime_SysReserve(want, bitmap_size + arena_size); + p = runtime_SysReserve(want, bitmap_size + spans_size + arena_size); if(p == nil) runtime_throw("runtime: cannot reserve arena virtual address space"); if((uintptr)p & (((uintptr)1<<PageShift)-1)) - runtime_printf("runtime: SysReserve returned unaligned address %p; asked for %p", p, bitmap_size+arena_size); + runtime_printf("runtime: SysReserve returned unaligned address %p; asked for %p", p, + bitmap_size+spans_size+arena_size); } if((uintptr)p & (((uintptr)1<<PageShift)-1)) runtime_throw("runtime: SysReserve returned unaligned address"); - runtime_mheap->bitmap = p; - runtime_mheap->arena_start = p + bitmap_size; - runtime_mheap->arena_used = runtime_mheap->arena_start; - runtime_mheap->arena_end = runtime_mheap->arena_start + arena_size; + runtime_mheap.spans = (MSpan**)p; + runtime_mheap.bitmap = p + spans_size; + runtime_mheap.arena_start = p + spans_size + bitmap_size; + runtime_mheap.arena_used = runtime_mheap.arena_start; + runtime_mheap.arena_end = runtime_mheap.arena_start + arena_size; // Initialize the rest of the allocator. - runtime_MHeap_Init(runtime_mheap, runtime_SysAlloc); + runtime_MHeap_Init(&runtime_mheap); runtime_m()->mcache = runtime_allocmcache(); // See if it works. @@ -463,8 +507,7 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n) uintptr needed; needed = (uintptr)h->arena_used + n - (uintptr)h->arena_end; - // Round wanted arena size to a multiple of 256MB. - needed = (needed + (256<<20) - 1) & ~((256<<20)-1); + needed = ROUND(needed, 256<<20); new_end = h->arena_end + needed; if(new_end <= h->arena_start + MaxArena32) { p = runtime_SysReserve(h->arena_end, new_end - h->arena_end); @@ -475,9 +518,10 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n) if(n <= (uintptr)(h->arena_end - h->arena_used)) { // Keep taking from our reservation. p = h->arena_used; - runtime_SysMap(p, n); + runtime_SysMap(p, n, &mstats.heap_sys); h->arena_used += n; runtime_MHeap_MapBits(h); + runtime_MHeap_MapSpans(h); if(raceenabled) runtime_racemapshadow(p, n); return p; @@ -490,14 +534,14 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n) // 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); + p = runtime_SysAlloc(n, &mstats.heap_sys); if(p == nil) return nil; if(p < h->arena_start || (uintptr)(p+n - 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); + runtime_SysFree(p, n, &mstats.heap_sys); return nil; } @@ -506,6 +550,7 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n) if(h->arena_used > h->arena_end) h->arena_end = h->arena_used; runtime_MHeap_MapBits(h); + runtime_MHeap_MapSpans(h); if(raceenabled) runtime_racemapshadow(p, n); } @@ -513,17 +558,68 @@ runtime_MHeap_SysAlloc(MHeap *h, uintptr n) return p; } +static struct +{ + Lock; + byte* pos; + byte* end; +} persistent; + +enum +{ + PersistentAllocChunk = 256<<10, + PersistentAllocMaxBlock = 64<<10, // VM reservation granularity is 64K on windows +}; + +// Wrapper around SysAlloc that can allocate small chunks. +// There is no associated free operation. +// Intended for things like function/type/debug-related persistent data. +// If align is 0, uses default align (currently 8). +void* +runtime_persistentalloc(uintptr size, uintptr align, uint64 *stat) +{ + byte *p; + + if(align != 0) { + if(align&(align-1)) + runtime_throw("persistentalloc: align is now a power of 2"); + if(align > PageSize) + runtime_throw("persistentalloc: align is too large"); + } else + align = 8; + if(size >= PersistentAllocMaxBlock) + return runtime_SysAlloc(size, stat); + runtime_lock(&persistent); + persistent.pos = (byte*)ROUND((uintptr)persistent.pos, align); + if(persistent.pos + size > persistent.end) { + persistent.pos = runtime_SysAlloc(PersistentAllocChunk, &mstats.other_sys); + if(persistent.pos == nil) { + runtime_unlock(&persistent); + runtime_throw("runtime: cannot allocate memory"); + } + persistent.end = persistent.pos + PersistentAllocChunk; + } + p = persistent.pos; + persistent.pos += size; + runtime_unlock(&persistent); + if(stat != &mstats.other_sys) { + // reaccount the allocation against provided stat + runtime_xadd64(stat, size); + runtime_xadd64(&mstats.other_sys, -(uint64)size); + } + return p; +} + static Lock settype_lock; void -runtime_settype_flush(M *mp, bool sysalloc) +runtime_settype_flush(M *mp) { uintptr *buf, *endbuf; uintptr size, ofs, j, t; uintptr ntypes, nbytes2, nbytes3; uintptr *data2; byte *data3; - bool sysalloc3; void *v; uintptr typ, p; MSpan *s; @@ -542,8 +638,8 @@ runtime_settype_flush(M *mp, bool sysalloc) // (Manually inlined copy of runtime_MHeap_Lookup) p = (uintptr)v>>PageShift; if(sizeof(void*) == 8) - p -= (uintptr)runtime_mheap->arena_start >> PageShift; - s = runtime_mheap->map[p]; + p -= (uintptr)runtime_mheap.arena_start >> PageShift; + s = runtime_mheap.spans[p]; if(s->sizeclass == 0) { s->types.compression = MTypes_Single; @@ -558,20 +654,9 @@ runtime_settype_flush(M *mp, bool sysalloc) case MTypes_Empty: ntypes = (s->npages << PageShift) / size; nbytes3 = 8*sizeof(uintptr) + 1*ntypes; - - if(!sysalloc) { - data3 = runtime_mallocgc(nbytes3, FlagNoProfiling|FlagNoPointers, 0, 1); - } else { - data3 = runtime_SysAlloc(nbytes3); - if(data3 == nil) - runtime_throw("runtime: cannot allocate memory"); - if(0) runtime_printf("settype(0->3): SysAlloc(%x) --> %p\n", (uint32)nbytes3, data3); - } - + data3 = runtime_mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC); s->types.compression = MTypes_Bytes; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data3; - ((uintptr*)data3)[1] = typ; data3[8*sizeof(uintptr) + ofs] = 1; break; @@ -596,20 +681,8 @@ runtime_settype_flush(M *mp, bool sysalloc) } else { ntypes = (s->npages << PageShift) / size; nbytes2 = ntypes * sizeof(uintptr); - - if(!sysalloc) { - data2 = runtime_mallocgc(nbytes2, FlagNoProfiling|FlagNoPointers, 0, 1); - } else { - data2 = runtime_SysAlloc(nbytes2); - if(data2 == nil) - runtime_throw("runtime: cannot allocate memory"); - if(0) runtime_printf("settype.(3->2): SysAlloc(%x) --> %p\n", (uint32)nbytes2, data2); - } - - sysalloc3 = s->types.sysalloc; - + data2 = runtime_mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC); s->types.compression = MTypes_Words; - s->types.sysalloc = sysalloc; s->types.data = (uintptr)data2; // Move the contents of data3 to data2. Then deallocate data3. @@ -618,12 +691,6 @@ runtime_settype_flush(M *mp, bool sysalloc) t = ((uintptr*)data3)[t]; data2[j] = t; } - if(sysalloc3) { - nbytes3 = 8*sizeof(uintptr) + 1*ntypes; - if(0) runtime_printf("settype.(3->2): SysFree(%p,%x)\n", data3, (uint32)nbytes3); - runtime_SysFree(data3, nbytes3); - } - data2[ofs] = typ; } break; @@ -634,64 +701,6 @@ runtime_settype_flush(M *mp, bool sysalloc) mp->settype_bufsize = 0; } -// It is forbidden to use this function if it is possible that -// explicit deallocation via calling runtime_free(v) may happen. -void -runtime_settype(void *v, uintptr t) -{ - M *mp; - uintptr *buf; - uintptr i; - MSpan *s; - - if(t == 0) - runtime_throw("settype: zero type"); - - mp = runtime_m(); - buf = mp->settype_buf; - i = mp->settype_bufsize; - buf[i+0] = (uintptr)v; - buf[i+1] = t; - i += 2; - mp->settype_bufsize = i; - - if(i == nelem(mp->settype_buf)) { - runtime_settype_flush(mp, false); - } - - if(DebugTypeAtBlockEnd) { - s = runtime_MHeap_Lookup(runtime_mheap, v); - *(uintptr*)((uintptr)v+s->elemsize-sizeof(uintptr)) = t; - } -} - -void -runtime_settype_sysfree(MSpan *s) -{ - uintptr ntypes, nbytes; - - if(!s->types.sysalloc) - return; - - nbytes = (uintptr)-1; - - switch (s->types.compression) { - case MTypes_Words: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = ntypes * sizeof(uintptr); - break; - case MTypes_Bytes: - ntypes = (s->npages << PageShift) / s->elemsize; - nbytes = 8*sizeof(uintptr) + 1*ntypes; - break; - } - - if(nbytes != (uintptr)-1) { - if(0) runtime_printf("settype: SysFree(%p,%x)\n", (void*)s->types.data, (uint32)nbytes); - runtime_SysFree((void*)s->types.data, nbytes); - } -} - uintptr runtime_gettype(void *v) { @@ -699,7 +708,7 @@ runtime_gettype(void *v) uintptr t, ofs; byte *data; - s = runtime_MHeap_LookupMaybe(runtime_mheap, v); + s = runtime_MHeap_LookupMaybe(&runtime_mheap, v); if(s != nil) { t = 0; switch(s->types.compression) { @@ -736,61 +745,23 @@ runtime_gettype(void *v) void* runtime_mal(uintptr n) { - return runtime_mallocgc(n, 0, 1, 1); + return runtime_mallocgc(n, 0, 0); } void * runtime_new(const Type *typ) { - void *ret; - uint32 flag; - - if(raceenabled) - runtime_m()->racepc = runtime_getcallerpc(&typ); - - if(typ->__size == 0) { - // All 0-length allocations use this pointer. - // The language does not require the allocations to - // have distinct values. - ret = (uint8*)&runtime_zerobase; - } else { - flag = typ->__code&GO_NO_POINTERS ? FlagNoPointers : 0; - ret = runtime_mallocgc(typ->__size, flag, 1, 1); - - if(UseSpanType && !flag) { - if(false) - runtime_printf("new %S: %p\n", *typ->__reflection, ret); - runtime_settype(ret, (uintptr)typ | TypeInfo_SingleObject); - } - } - - return ret; + return runtime_mallocgc(typ->__size, (uintptr)typ | TypeInfo_SingleObject, typ->kind&KindNoPointers ? FlagNoScan : 0); } static void* cnew(const Type *typ, intgo n, int32 objtyp) { - uint32 flag; - void *ret; - if((objtyp&(PtrSize-1)) != objtyp) runtime_throw("runtime: invalid objtyp"); if(n < 0 || (typ->__size > 0 && (uintptr)n > (MaxMem/typ->__size))) runtime_panicstring("runtime: allocation size out of range"); - if(typ->__size == 0 || n == 0) { - // All 0-length allocations use this pointer. - // The language does not require the allocations to - // have distinct values. - return &runtime_zerobase; - } - flag = typ->__code&GO_NO_POINTERS ? FlagNoPointers : 0; - ret = runtime_mallocgc(typ->__size*n, flag, 1, 1); - if(UseSpanType && !flag) { - if(false) - runtime_printf("cnew [%D]%S: %p\n", (int64)n, *typ->__reflection, ret); - runtime_settype(ret, (uintptr)typ | TypeInfo_SingleObject); - } - return ret; + return runtime_mallocgc(typ->__size*n, (uintptr)typ | objtyp, typ->kind&KindNoPointers ? FlagNoScan : 0); } // same as runtime_new, but callable from C @@ -814,6 +785,8 @@ func SetFinalizer(obj Eface, finalizer Eface) { byte *base; uintptr size; const FuncType *ft; + const Type *fint; + const PtrType *ot; if(obj.__type_descriptor == nil) { runtime_printf("runtime.SetFinalizer: first argument is nil interface\n"); @@ -828,22 +801,36 @@ func SetFinalizer(obj Eface, finalizer Eface) { goto throw; } ft = nil; + ot = (const PtrType*)obj.__type_descriptor; + fint = nil; if(finalizer.__type_descriptor != nil) { if(finalizer.__type_descriptor->__code != GO_FUNC) goto badfunc; ft = (const FuncType*)finalizer.__type_descriptor; - if(ft->__dotdotdot || ft->__in.__count != 1 || !__go_type_descriptors_equal(*(Type**)ft->__in.__values, obj.__type_descriptor)) + if(ft->__dotdotdot || ft->__in.__count != 1) + goto badfunc; + fint = *(Type**)ft->__in.__values; + if(__go_type_descriptors_equal(fint, obj.__type_descriptor)) { + // ok - same type + } else if(fint->__code == GO_PTR && (fint->__uncommon == nil || fint->__uncommon->__name == nil || obj.type->__uncommon == nil || obj.type->__uncommon->__name == nil) && __go_type_descriptors_equal(((const PtrType*)fint)->__element_type, ((const PtrType*)obj.type)->__element_type)) { + // ok - not same type, but both pointers, + // one or the other is unnamed, and same element type, so assignable. + } else if(fint->kind == GO_INTERFACE && ((const InterfaceType*)fint)->__methods.__count == 0) { + // ok - satisfies empty interface + } else if(fint->kind == GO_INTERFACE && __go_convert_interface_2(fint, obj.__type_descriptor, 1) != nil) { + // ok - satisfies non-empty interface + } else goto badfunc; } - if(!runtime_addfinalizer(obj.__object, finalizer.__type_descriptor != nil ? *(void**)finalizer.__object : nil, ft)) { + if(!runtime_addfinalizer(obj.__object, finalizer.__type_descriptor != nil ? *(void**)finalizer.__object : nil, ft, ot)) { runtime_printf("runtime.SetFinalizer: finalizer already set\n"); goto throw; } return; badfunc: - runtime_printf("runtime.SetFinalizer: second argument is %S, not func(%S)\n", *finalizer.__type_descriptor->__reflection, *obj.__type_descriptor->__reflection); + runtime_printf("runtime.SetFinalizer: cannot pass %S to finalizer %S\n", *obj.__type_descriptor->__reflection, *finalizer.__type_descriptor->__reflection); throw: runtime_throw("runtime.SetFinalizer"); } diff --git a/libgo/runtime/malloc.h b/libgo/runtime/malloc.h index ebea34e..45c4c09 100644 --- a/libgo/runtime/malloc.h +++ b/libgo/runtime/malloc.h @@ -108,9 +108,7 @@ enum // Tunable constants. MaxSmallSize = 32<<10, - FixAllocChunk = 128<<10, // Chunk size for FixAlloc - MaxMCacheListLen = 256, // Maximum objects on MCacheList - MaxMCacheSize = 2<<20, // Maximum bytes in one MCache + FixAllocChunk = 16<<10, // Chunk size for FixAlloc MaxMHeapList = 1<<(20 - PageShift), // Maximum page length for fixed-size list in MHeap. HeapAllocChunk = 1<<20, // Chunk size for heap growth @@ -155,13 +153,13 @@ 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. If the pointer argument is non-nil, the caller -// wants a mapping there or nowhere. +// or a megabyte. // // SysUnused notifies the operating system that the contents // of the memory region are no longer needed and can be reused -// for other purposes. The program reserves the right to start -// accessing those pages in the future. +// for other purposes. +// SysUsed notifies the operating system that the contents +// of the memory region are needed again. // // SysFree returns it unconditionally; this is only used if // an out-of-memory error has been detected midway through @@ -174,10 +172,11 @@ struct MLink // // SysMap maps previously reserved address space for use. -void* runtime_SysAlloc(uintptr nbytes); -void runtime_SysFree(void *v, uintptr nbytes); +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_SysMap(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); // FixAlloc is a simple free-list allocator for fixed size objects. @@ -190,18 +189,17 @@ void* runtime_SysReserve(void *v, uintptr nbytes); // smashed by freeing and reallocating. struct FixAlloc { - uintptr size; - void *(*alloc)(uintptr); - void (*first)(void *arg, byte *p); // called first time p is returned - void *arg; - MLink *list; - byte *chunk; - uint32 nchunk; - uintptr inuse; // in-use bytes now - uintptr sys; // bytes obtained from system + uintptr size; + void (*first)(void *arg, byte *p); // called first time p is returned + void* arg; + MLink* list; + byte* chunk; + uint32 nchunk; + uintptr inuse; // in-use bytes now + uint64* stat; }; -void runtime_FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg); +void runtime_FixAlloc_Init(FixAlloc *f, uintptr size, void (*first)(void*, byte*), void *arg, uint64 *stat); void* runtime_FixAlloc_Alloc(FixAlloc *f); void runtime_FixAlloc_Free(FixAlloc *f, void *p); @@ -236,6 +234,8 @@ struct MStats uint64 mcache_inuse; // MCache structures uint64 mcache_sys; uint64 buckhash_sys; // profiling bucket hash table + uint64 gc_sys; + uint64 other_sys; // Statistics about garbage collector. // Protected by mheap or stopping the world during GC. @@ -267,14 +267,12 @@ extern MStats mstats // class_to_size[i] = largest size in class i // class_to_allocnpages[i] = number of pages to allocate when // making new objects in class i -// class_to_transfercount[i] = number of objects to move when -// taking a bunch of objects out of the central lists -// and putting them in the thread free list. int32 runtime_SizeToClass(int32); extern int32 runtime_class_to_size[NumSizeClasses]; extern int32 runtime_class_to_allocnpages[NumSizeClasses]; -extern int32 runtime_class_to_transfercount[NumSizeClasses]; +extern int8 runtime_size_to_class8[1024/8 + 1]; +extern int8 runtime_size_to_class128[(MaxSmallSize-1024)/128 + 1]; extern void runtime_InitSizes(void); @@ -285,30 +283,24 @@ struct MCacheList { MLink *list; uint32 nlist; - uint32 nlistmin; }; struct MCache { - MCacheList list[NumSizeClasses]; - uintptr size; + // The following members are accessed on every malloc, + // so they are grouped here for better caching. + int32 next_sample; // trigger heap sample after allocating this many bytes intptr local_cachealloc; // bytes allocated (or freed) from cache since last lock of heap - intptr local_objects; // objects allocated (or freed) from cache since last lock of heap - intptr local_alloc; // bytes allocated (or freed) since last lock of heap - uintptr local_total_alloc; // bytes allocated (even if freed) since last lock of heap - uintptr local_nmalloc; // number of mallocs since last lock of heap - uintptr local_nfree; // number of frees since last lock of heap - uintptr local_nlookup; // number of pointer lookups since last lock of heap - int32 next_sample; // trigger heap sample after allocating this many bytes - // Statistics about allocation size classes since last lock of heap - struct { - uintptr nmalloc; - uintptr nfree; - } local_by_size[NumSizeClasses]; - + // The rest is not accessed on every malloc. + MCacheList list[NumSizeClasses]; + // Local allocator stats, flushed during GC. + uintptr local_nlookup; // number of pointer lookups + uintptr local_largefree; // bytes freed for large objects (>MaxSmallSize) + uintptr local_nlargefree; // number of frees for large objects (>MaxSmallSize) + uintptr local_nsmallfree[NumSizeClasses]; // number of frees for small objects (<=MaxSmallSize) }; -void* runtime_MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zeroed); +void runtime_MCache_Refill(MCache *c, int32 sizeclass); void runtime_MCache_Free(MCache *c, void *p, int32 sizeclass, uintptr size); void runtime_MCache_ReleaseAll(MCache *c); @@ -346,7 +338,6 @@ enum struct MTypes { byte compression; // one of MTypes_* - bool sysalloc; // whether (void*)data is from runtime_SysAlloc uintptr data; }; @@ -397,8 +388,8 @@ struct MCentral }; void runtime_MCentral_Init(MCentral *c, int32 sizeclass); -int32 runtime_MCentral_AllocList(MCentral *c, int32 n, MLink **first); -void runtime_MCentral_FreeList(MCentral *c, int32 n, MLink *first); +int32 runtime_MCentral_AllocList(MCentral *c, MLink **first); +void runtime_MCentral_FreeList(MCentral *c, MLink *first); void runtime_MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end); // Main malloc heap. @@ -414,7 +405,8 @@ struct MHeap uint32 nspancap; // span lookup - MSpan *map[1<<MHeapMap_Bits]; + MSpan** spans; + uintptr spans_mapped; // range of addresses we might see in the heap byte *bitmap; @@ -434,10 +426,15 @@ struct MHeap FixAlloc spanalloc; // allocator for Span* FixAlloc cachealloc; // allocator for MCache* + + // Malloc stats. + uint64 largefree; // bytes freed for large objects (>MaxSmallSize) + uint64 nlargefree; // number of frees for large objects (>MaxSmallSize) + uint64 nsmallfree[NumSizeClasses]; // number of frees for small objects (<=MaxSmallSize) }; -extern MHeap *runtime_mheap; +extern MHeap runtime_mheap; -void runtime_MHeap_Init(MHeap *h, void *(*allocator)(uintptr)); +void runtime_MHeap_Init(MHeap *h); MSpan* runtime_MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32 zeroed); void runtime_MHeap_Free(MHeap *h, MSpan *s, int32 acct); MSpan* runtime_MHeap_Lookup(MHeap *h, void *v); @@ -445,9 +442,11 @@ MSpan* runtime_MHeap_LookupMaybe(MHeap *h, void *v); void runtime_MGetSizeClassInfo(int32 sizeclass, uintptr *size, int32 *npages, int32 *nobj); void* runtime_MHeap_SysAlloc(MHeap *h, uintptr n); void runtime_MHeap_MapBits(MHeap *h); +void runtime_MHeap_MapSpans(MHeap *h); void runtime_MHeap_Scavenger(void*); -void* runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed); +void* runtime_mallocgc(uintptr size, uintptr typ, uint32 flag); +void* runtime_persistentalloc(uintptr size, uintptr align, uint64 *stat); int32 runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **s); void runtime_gc(int32 force); void runtime_markallocated(void *v, uintptr n, bool noptr); @@ -463,17 +462,18 @@ void runtime_purgecachedstats(MCache*); void* runtime_cnew(const Type*); void* runtime_cnewarray(const Type*, intgo); -void runtime_settype(void*, uintptr); -void runtime_settype_flush(M*, bool); +void runtime_settype_flush(M*); void runtime_settype_sysfree(MSpan*); uintptr runtime_gettype(void*); enum { // flags to malloc - FlagNoPointers = 1<<0, // no pointers here - FlagNoProfiling = 1<<1, // must not profile - FlagNoGC = 1<<2, // must not free or scan for pointers + FlagNoScan = 1<<0, // GC doesn't have to scan object + FlagNoProfiling = 1<<1, // must not profile + FlagNoGC = 1<<2, // must not free or scan for pointers + FlagNoZero = 1<<3, // don't zero memory + FlagNoInvokeGC = 1<<4, // don't invoke GC }; typedef struct Obj Obj; @@ -493,15 +493,15 @@ void runtime_helpgc(int32 nproc); void runtime_gchelper(void); struct __go_func_type; -bool runtime_getfinalizer(void *p, bool del, FuncVal **fn, const struct __go_func_type **ft); +struct __go_ptr_type; +bool runtime_getfinalizer(void *p, bool del, FuncVal **fn, const struct __go_func_type **ft, const struct __go_ptr_type **ot); void runtime_walkfintab(void (*fn)(void*), void (*scan)(Obj)); enum { TypeInfo_SingleObject = 0, TypeInfo_Array = 1, - TypeInfo_Map = 2, - TypeInfo_Chan = 3, + TypeInfo_Chan = 2, // Enables type information at the end of blocks allocated from heap DebugTypeAtBlockEnd = 0, diff --git a/libgo/runtime/mcache.c b/libgo/runtime/mcache.c index 45bac4f..38f824a 100644 --- a/libgo/runtime/mcache.c +++ b/libgo/runtime/mcache.c @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Per-thread (in Go, per-M) malloc cache for small objects. +// Per-P malloc cache for small objects. // // See malloc.h for an overview. @@ -10,48 +10,23 @@ #include "arch.h" #include "malloc.h" -void* -runtime_MCache_Alloc(MCache *c, int32 sizeclass, uintptr size, int32 zeroed) +void +runtime_MCache_Refill(MCache *c, int32 sizeclass) { MCacheList *l; - MLink *first, *v; - int32 n; - // Allocate from list. + // Replenish using central lists. l = &c->list[sizeclass]; - if(l->list == nil) { - // Replenish using central lists. - n = runtime_MCentral_AllocList(&runtime_mheap->central[sizeclass], - runtime_class_to_transfercount[sizeclass], &first); - if(n == 0) - runtime_throw("out of memory"); - l->list = first; - l->nlist = n; - c->size += n*size; - } - v = l->list; - l->list = v->next; - l->nlist--; - if(l->nlist < l->nlistmin) - l->nlistmin = l->nlist; - c->size -= size; - - // v is zeroed except for the link pointer - // that we used above; zero that. - v->next = nil; - if(zeroed) { - // block is zeroed iff second word is zero ... - if(size > sizeof(uintptr) && ((uintptr*)v)[1] != 0) - runtime_memclr((byte*)v, size); - } - c->local_cachealloc += size; - c->local_objects++; - return v; + 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"); } // Take n elements off l and return them to the central free list. static void -ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass) +ReleaseN(MCacheList *l, int32 n, int32 sizeclass) { MLink *first, **lp; int32 i; @@ -64,18 +39,14 @@ ReleaseN(MCache *c, MCacheList *l, int32 n, int32 sizeclass) l->list = *lp; *lp = nil; l->nlist -= n; - if(l->nlist < l->nlistmin) - l->nlistmin = l->nlist; - c->size -= n*runtime_class_to_size[sizeclass]; // Return them to central free list. - runtime_MCentral_FreeList(&runtime_mheap->central[sizeclass], n, first); + runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass], first); } void runtime_MCache_Free(MCache *c, void *v, int32 sizeclass, uintptr size) { - int32 i, n; MCacheList *l; MLink *p; @@ -85,34 +56,12 @@ runtime_MCache_Free(MCache *c, void *v, int32 sizeclass, uintptr size) p->next = l->list; l->list = p; l->nlist++; - c->size += size; c->local_cachealloc -= size; - c->local_objects--; - - if(l->nlist >= MaxMCacheListLen) { - // Release a chunk back. - ReleaseN(c, l, runtime_class_to_transfercount[sizeclass], sizeclass); - } - - if(c->size >= MaxMCacheSize) { - // Scavenge. - for(i=0; i<NumSizeClasses; i++) { - l = &c->list[i]; - n = l->nlistmin; - // n is the minimum number of elements we've seen on - // the list since the last scavenge. If n > 0, it means that - // we could have gotten by with n fewer elements - // without needing to consult the central free list. - // Move toward that situation by releasing n/2 of them. - if(n > 0) { - if(n > 1) - n /= 2; - ReleaseN(c, l, n, i); - } - l->nlistmin = l->nlist; - } - } + // 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); } void @@ -123,7 +72,10 @@ runtime_MCache_ReleaseAll(MCache *c) for(i=0; i<NumSizeClasses; i++) { l = &c->list[i]; - ReleaseN(c, l, l->nlist, i); - l->nlistmin = 0; + if(l->list) { + runtime_MCentral_FreeList(&runtime_mheap.central[i], l->list); + l->list = nil; + l->nlist = 0; + } } } diff --git a/libgo/runtime/mcentral.c b/libgo/runtime/mcentral.c index b3108a1..8191610 100644 --- a/libgo/runtime/mcentral.c +++ b/libgo/runtime/mcentral.c @@ -30,16 +30,15 @@ runtime_MCentral_Init(MCentral *c, int32 sizeclass) runtime_MSpanList_Init(&c->empty); } -// Allocate up to n objects from the central free list. +// 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, *pstart points at the first object. +// On return, *pfirst points at the first object. int32 -runtime_MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst) +runtime_MCentral_AllocList(MCentral *c, MLink **pfirst) { MSpan *s; - MLink *first, *last; - int32 cap, avail, i; + int32 cap, n; runtime_lock(c); // Replenish central list if empty. @@ -52,49 +51,27 @@ runtime_MCentral_AllocList(MCentral *c, int32 n, MLink **pfirst) } s = c->nonempty.next; cap = (s->npages << PageShift) / s->elemsize; - avail = cap - s->ref; - if(avail < n) - n = avail; - - // First one is guaranteed to work, because we just grew the list. - first = s->freelist; - last = first; - for(i=1; i<n; i++) { - last = last->next; - } - s->freelist = last->next; - last->next = nil; + n = cap - s->ref; + *pfirst = s->freelist; + s->freelist = nil; s->ref += n; c->nfree -= n; - - if(n == avail) { - if(s->freelist != nil || s->ref != (uint32)cap) { - runtime_throw("invalid freelist"); - } - runtime_MSpanList_Remove(s); - runtime_MSpanList_Insert(&c->empty, s); - } - + runtime_MSpanList_Remove(s); + runtime_MSpanList_Insert(&c->empty, s); runtime_unlock(c); - *pfirst = first; return n; } -// Free n objects back into the central free list. +// Free the list of objects back into the central free list. void -runtime_MCentral_FreeList(MCentral *c, int32 n, MLink *start) +runtime_MCentral_FreeList(MCentral *c, MLink *start) { - MLink *v, *next; - - // Assume next == nil marks end of list. - // n and end would be useful if we implemented - // the transfer cache optimization in the TODO above. - USED(n); + MLink *next; runtime_lock(c); - for(v=start; v; v=next) { - next = v->next; - MCentral_Free(c, v); + for(; start != nil; start = next) { + next = start->next; + MCentral_Free(c, start); } runtime_unlock(c); } @@ -108,7 +85,7 @@ MCentral_Free(MCentral *c, void *v) int32 size; // Find span for v. - s = runtime_MHeap_Lookup(runtime_mheap, v); + s = runtime_MHeap_Lookup(&runtime_mheap, v); if(s == nil || s->ref == 0) runtime_throw("invalid free"); @@ -133,7 +110,7 @@ MCentral_Free(MCentral *c, void *v) s->freelist = nil; c->nfree -= (s->npages << PageShift) / size; runtime_unlock(c); - runtime_MHeap_Free(runtime_mheap, s, 0); + runtime_MHeap_Free(&runtime_mheap, s, 0); runtime_lock(c); } } @@ -168,7 +145,7 @@ runtime_MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *e 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); + runtime_MHeap_Free(&runtime_mheap, s, 0); } else { runtime_unlock(c); } @@ -200,7 +177,7 @@ MCentral_Grow(MCentral *c) runtime_unlock(c); runtime_MGetSizeClassInfo(c->sizeclass, &size, &npages, &n); - s = runtime_MHeap_Alloc(runtime_mheap, npages, c->sizeclass, 0, 1); + s = runtime_MHeap_Alloc(&runtime_mheap, npages, c->sizeclass, 0, 1); if(s == nil) { // TODO(rsc): Log out of memory runtime_lock(c); diff --git a/libgo/runtime/mem.c b/libgo/runtime/mem.c index 8481e95..78f7c51f 100644 --- a/libgo/runtime/mem.c +++ b/libgo/runtime/mem.c @@ -60,13 +60,11 @@ mmap_fixed(byte *v, uintptr n, int32 prot, int32 flags, int32 fd, uint32 offset) } void* -runtime_SysAlloc(uintptr n) +runtime_SysAlloc(uintptr n, uint64 *stat) { void *p; int fd = -1; - mstats.sys += n; - #ifdef USE_DEV_ZERO if (dev_zero == -1) { dev_zero = open("/dev/zero", O_RDONLY); @@ -91,6 +89,7 @@ runtime_SysAlloc(uintptr n) } return nil; } + runtime_xadd64(stat, n); return p; } @@ -103,9 +102,16 @@ runtime_SysUnused(void *v __attribute__ ((unused)), uintptr n __attribute__ ((un } void -runtime_SysFree(void *v, uintptr n) +runtime_SysUsed(void *v, uintptr n) +{ + USED(v); + USED(n); +} + +void +runtime_SysFree(void *v, uintptr n, uint64 *stat) { - mstats.sys -= n; + runtime_xadd64(stat, -(uint64)n); runtime_munmap(v, n); } @@ -132,8 +138,10 @@ runtime_SysReserve(void *v, uintptr n) // Only user-mode Linux (UML) rejects these requests. if(sizeof(void*) == 8 && (uintptr)v >= 0xffffffffU) { p = mmap_fixed(v, 64<<10, PROT_NONE, MAP_ANON|MAP_PRIVATE, fd, 0); - if (p != v) + if (p != v) { + runtime_munmap(p, 64<<10); return nil; + } runtime_munmap(p, 64<<10); return v; } @@ -149,12 +157,12 @@ runtime_SysReserve(void *v, uintptr n) } void -runtime_SysMap(void *v, uintptr n) +runtime_SysMap(void *v, uintptr n, uint64 *stat) { void *p; int fd = -1; - mstats.sys += n; + runtime_xadd64(stat, n); #ifdef USE_DEV_ZERO if (dev_zero == -1) { diff --git a/libgo/runtime/mfinal.c b/libgo/runtime/mfinal.c index 407092b..625af52 100644 --- a/libgo/runtime/mfinal.c +++ b/libgo/runtime/mfinal.c @@ -5,6 +5,7 @@ #include "runtime.h" #include "arch.h" #include "malloc.h" +#include "go-type.h" enum { debug = 0 }; @@ -13,6 +14,7 @@ struct Fin { FuncVal *fn; const struct __go_func_type *ft; + const struct __go_ptr_type *ot; }; // Finalizer hash table. Direct hash, linear scan, at most 3/4 full. @@ -42,7 +44,7 @@ static struct { } fintab[TABSZ]; static void -addfintab(Fintab *t, void *k, FuncVal *fn, const struct __go_func_type *ft) +addfintab(Fintab *t, void *k, FuncVal *fn, const struct __go_func_type *ft, const struct __go_ptr_type *ot) { int32 i, j; @@ -67,6 +69,7 @@ ret: t->fkey[i] = k; t->val[i].fn = fn; t->val[i].ft = ft; + t->val[i].ot = ot; } static bool @@ -87,6 +90,7 @@ lookfintab(Fintab *t, void *k, bool del, Fin *f) t->fkey[i] = (void*)-1; t->val[i].fn = nil; t->val[i].ft = nil; + t->val[i].ot = nil; t->ndead++; } return true; @@ -117,13 +121,13 @@ resizefintab(Fintab *tab) newtab.max *= 3; } - newtab.fkey = runtime_mallocgc(newtab.max*sizeof newtab.fkey[0], FlagNoPointers, 0, 1); - newtab.val = runtime_mallocgc(newtab.max*sizeof newtab.val[0], 0, 0, 1); + newtab.fkey = runtime_mallocgc(newtab.max*sizeof newtab.fkey[0], 0, FlagNoInvokeGC|FlagNoScan); + newtab.val = runtime_mallocgc(newtab.max*sizeof newtab.val[0], 0, FlagNoInvokeGC); for(i=0; i<tab->max; i++) { k = tab->fkey[i]; if(k != nil && k != (void*)-1) - addfintab(&newtab, k, tab->val[i].fn, tab->val[i].ft); + addfintab(&newtab, k, tab->val[i].fn, tab->val[i].ft, tab->val[i].ot); } runtime_free(tab->fkey); @@ -137,7 +141,7 @@ resizefintab(Fintab *tab) } bool -runtime_addfinalizer(void *p, FuncVal *f, const struct __go_func_type *ft) +runtime_addfinalizer(void *p, FuncVal *f, const struct __go_func_type *ft, const struct __go_ptr_type *ot) { Fintab *tab; byte *base; @@ -166,7 +170,7 @@ runtime_addfinalizer(void *p, FuncVal *f, const struct __go_func_type *ft) resizefintab(tab); } - addfintab(tab, p, f, ft); + addfintab(tab, p, f, ft, ot); runtime_setblockspecial(p, true); runtime_unlock(tab); return true; @@ -175,7 +179,7 @@ runtime_addfinalizer(void *p, FuncVal *f, const struct __go_func_type *ft) // get finalizer; if del, delete finalizer. // caller is responsible for updating RefHasFinalizer (special) bit. bool -runtime_getfinalizer(void *p, bool del, FuncVal **fn, const struct __go_func_type **ft) +runtime_getfinalizer(void *p, bool del, FuncVal **fn, const struct __go_func_type **ft, const struct __go_ptr_type **ot) { Fintab *tab; bool res; @@ -189,6 +193,7 @@ runtime_getfinalizer(void *p, bool del, FuncVal **fn, const struct __go_func_typ return false; *fn = f.fn; *ft = f.ft; + *ot = f.ot; return true; } diff --git a/libgo/runtime/mfixalloc.c b/libgo/runtime/mfixalloc.c index 6e4f0c6..9d0b3bb 100644 --- a/libgo/runtime/mfixalloc.c +++ b/libgo/runtime/mfixalloc.c @@ -13,17 +13,16 @@ // Initialize f to allocate objects of the given size, // using the allocator to obtain chunks of memory. void -runtime_FixAlloc_Init(FixAlloc *f, uintptr size, void *(*alloc)(uintptr), void (*first)(void*, byte*), void *arg) +runtime_FixAlloc_Init(FixAlloc *f, uintptr size, void (*first)(void*, byte*), void *arg, uint64 *stat) { f->size = size; - f->alloc = alloc; f->first = first; f->arg = arg; f->list = nil; f->chunk = nil; f->nchunk = 0; f->inuse = 0; - f->sys = 0; + f->stat = stat; } void* @@ -43,10 +42,7 @@ runtime_FixAlloc_Alloc(FixAlloc *f) return v; } if(f->nchunk < f->size) { - f->sys += FixAllocChunk; - f->chunk = f->alloc(FixAllocChunk); - if(f->chunk == nil) - runtime_throw("out of memory (FixAlloc)"); + f->chunk = runtime_persistentalloc(FixAllocChunk, 0, f->stat); f->nchunk = FixAllocChunk; } v = f->chunk; diff --git a/libgo/runtime/mgc0.c b/libgo/runtime/mgc0.c index c3b3211..3edcee9 100644 --- a/libgo/runtime/mgc0.c +++ b/libgo/runtime/mgc0.c @@ -59,6 +59,13 @@ enum { PRECISE = 1, LOOP = 2, PC_BITS = PRECISE | LOOP, + + // Pointer map + BitsPerPointer = 2, + BitsNoPointer = 0, + BitsPointer = 1, + BitsIface = 2, + BitsEface = 3, }; // Bits in per-word bitmap. @@ -70,7 +77,7 @@ enum { // 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 bitNoPointers/bitBlockBoundary bits, then the 16 bitAllocated bits. +// then the 16 bitNoScan/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 @@ -87,7 +94,7 @@ enum { // /* then test bits & bitAllocated, bits & bitMarked, etc. */ // #define bitAllocated ((uintptr)1<<(bitShift*0)) -#define bitNoPointers ((uintptr)1<<(bitShift*1)) /* when bitAllocated is set */ +#define bitNoScan ((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 */ @@ -109,8 +116,6 @@ enum { // uint32 runtime_worldsema = 1; -static int32 gctrace; - // The size of Workbuf is N*PageSize. typedef struct Workbuf Workbuf; struct Workbuf @@ -129,6 +134,7 @@ struct Finalizer FuncVal *fn; void *arg; const struct __go_func_type *ft; + const struct __go_ptr_type *ot; }; typedef struct FinBlock FinBlock; @@ -178,7 +184,6 @@ static struct { enum { GC_DEFAULT_PTR = GC_NUM_INSTR, - GC_MAP_NEXT, GC_CHAN, GC_NUM_INSTR2 @@ -201,6 +206,16 @@ static struct { uint64 instr[GC_NUM_INSTR2]; uint64 putempty; uint64 getfull; + struct { + uint64 foundbit; + uint64 foundword; + uint64 foundspan; + } flushptrbuf; + struct { + uint64 foundbit; + uint64 foundword; + uint64 foundspan; + } markonly; } gcstats; // markonly marks an object. It returns true if the object @@ -210,12 +225,12 @@ static bool markonly(void *obj) { byte *p; - uintptr *bitp, bits, shift, x, xbits, off; + uintptr *bitp, bits, shift, x, xbits, off, j; MSpan *s; PageID k; // Words outside the arena cannot be pointers. - if((byte*)obj < runtime_mheap->arena_start || (byte*)obj >= runtime_mheap->arena_used) + if((byte*)obj < runtime_mheap.arena_start || (byte*)obj >= runtime_mheap.arena_used) return false; // obj may be a pointer to a live object. @@ -225,42 +240,57 @@ markonly(void *obj) obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1)); // Find bits for this word. - off = (uintptr*)obj - (uintptr*)runtime_mheap->arena_start; - bitp = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)obj - (uintptr*)runtime_mheap.arena_start; + bitp = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; xbits = *bitp; bits = xbits >> shift; // Pointing at the beginning of a block? - if((bits & (bitAllocated|bitBlockBoundary)) != 0) + if((bits & (bitAllocated|bitBlockBoundary)) != 0) { + if(CollectStats) + runtime_xadd64(&gcstats.markonly.foundbit, 1); goto found; + } + + // Pointing just past the beginning? + // Scan backward a little to find a block boundary. + for(j=shift; j-->0; ) { + if(((xbits>>j) & (bitAllocated|bitBlockBoundary)) != 0) { + shift = j; + bits = xbits>>shift; + if(CollectStats) + runtime_xadd64(&gcstats.markonly.foundword, 1); + goto found; + } + } // Otherwise consult span table to find beginning. // (Manually inlined copy of MHeap_LookupMaybe.) k = (uintptr)obj>>PageShift; x = k; if(sizeof(void*) == 8) - x -= (uintptr)runtime_mheap->arena_start>>PageShift; - s = runtime_mheap->map[x]; - if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse) + 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) return false; p = (byte*)((uintptr)s->start<<PageShift); if(s->sizeclass == 0) { obj = p; } else { - if((byte*)obj >= (byte*)s->limit) - return false; uintptr size = s->elemsize; int32 i = ((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; - bitp = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)obj - (uintptr*)runtime_mheap.arena_start; + bitp = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; xbits = *bitp; bits = xbits >> shift; + if(CollectStats) + runtime_xadd64(&gcstats.markonly.foundspan, 1); found: // Now we have bits, bitp, and shift correct for @@ -338,7 +368,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf Workbuf *wbuf; PtrTarget *ptrbuf_end; - arena_start = runtime_mheap->arena_start; + arena_start = runtime_mheap.arena_start; wp = *_wp; wbuf = *_wbuf; @@ -377,7 +407,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf // obj belongs to interval [mheap.arena_start, mheap.arena_used). if(Debug > 1) { - if(obj < runtime_mheap->arena_start || obj >= runtime_mheap->arena_used) + if(obj < runtime_mheap.arena_start || obj >= runtime_mheap.arena_used) runtime_throw("object is outside of mheap"); } @@ -398,8 +428,11 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf bits = xbits >> shift; // Pointing at the beginning of a block? - if((bits & (bitAllocated|bitBlockBoundary)) != 0) + if((bits & (bitAllocated|bitBlockBoundary)) != 0) { + if(CollectStats) + runtime_xadd64(&gcstats.flushptrbuf.foundbit, 1); goto found; + } ti = 0; @@ -410,6 +443,8 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf obj = (byte*)obj - (shift-j)*PtrSize; shift = j; bits = xbits>>shift; + if(CollectStats) + runtime_xadd64(&gcstats.flushptrbuf.foundword, 1); goto found; } } @@ -420,15 +455,13 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf x = k; if(sizeof(void*) == 8) x -= (uintptr)arena_start>>PageShift; - s = runtime_mheap->map[x]; - if(s == nil || k < s->start || k - s->start >= s->npages || s->state != MSpanInUse) + s = runtime_mheap.spans[x]; + if(s == nil || k < s->start || obj >= s->limit || s->state != MSpanInUse) continue; p = (byte*)((uintptr)s->start<<PageShift); if(s->sizeclass == 0) { obj = p; } else { - if((byte*)obj >= (byte*)s->limit) - continue; size = s->elemsize; int32 i = ((byte*)obj - p)/size; obj = p+i*size; @@ -440,6 +473,8 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf shift = off % wordsPerBitmapWord; xbits = *bitp; bits = xbits >> shift; + if(CollectStats) + runtime_xadd64(&gcstats.flushptrbuf.foundspan, 1); found: // Now we have bits, bitp, and shift correct for @@ -460,7 +495,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf } // If object has no pointers, don't need to scan further. - if((bits & bitNoPointers) != 0) + if((bits & bitNoScan) != 0) continue; // Ask span about size class. @@ -468,7 +503,7 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf x = (uintptr)obj >> PageShift; if(sizeof(void*) == 8) x -= (uintptr)arena_start>>PageShift; - s = runtime_mheap->map[x]; + s = runtime_mheap.spans[x]; PREFETCH(obj); @@ -552,9 +587,6 @@ flushobjbuf(Obj *objbuf, Obj **objbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_ static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR}; #if 0 -// Hashmap iterator program -static uintptr mapProg[2] = {0, GC_MAP_NEXT}; - // Hchan program static uintptr chanProg[2] = {0, GC_CHAN}; #endif @@ -578,7 +610,7 @@ checkptr(void *obj, uintptr objti) if(!Debug) runtime_throw("checkptr is debug only"); - if((byte*)obj < runtime_mheap->arena_start || (byte*)obj >= runtime_mheap->arena_used) + if((byte*)obj < runtime_mheap.arena_start || (byte*)obj >= runtime_mheap.arena_used) return; type = runtime_gettype(obj); t = (Type*)(type & ~(uintptr)(PtrSize-1)); @@ -586,8 +618,8 @@ checkptr(void *obj, uintptr objti) return; x = (uintptr)obj >> PageShift; if(sizeof(void*) == 8) - x -= (uintptr)(runtime_mheap->arena_start)>>PageShift; - s = runtime_mheap->map[x]; + x -= (uintptr)(runtime_mheap.arena_start)>>PageShift; + s = runtime_mheap.spans[x]; objstart = (byte*)((uintptr)s->start<<PageShift); if(s->sizeclass != 0) { i = ((byte*)obj - objstart)/s->elemsize; @@ -595,8 +627,11 @@ checkptr(void *obj, uintptr objti) } tisize = *(uintptr*)objti; // Sanity check for object size: it should fit into the memory block. - if((byte*)obj + tisize > objstart + s->elemsize) + if((byte*)obj + tisize > objstart + s->elemsize) { + runtime_printf("object of type '%S' at %p/%p does not fit in block %p/%p\n", + *t->string, obj, tisize, objstart, s->elemsize); runtime_throw("invalid gc type info"); + } if(obj != objstart) return; // If obj points to the beginning of the memory block, @@ -613,7 +648,7 @@ checkptr(void *obj, uintptr objti) 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]); + t->string ? (const int8*)t->string->str : (const int8*)"?", j, pc1[j], pc2[j]); runtime_throw("invalid gc type info"); } } @@ -638,7 +673,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) uintptr n, i, end_b, elemsize, size, ti, objti, count /* , type */; uintptr *pc, precise_type, nominal_size; #if 0 - uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret, chancap; + uintptr *chan_ret, chancap; #endif void *obj; const Type *t; @@ -650,11 +685,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) Eface *eface; Iface *iface; #if 0 - Hmap *hmap; - MapType *maptype; - bool mapkey_kind, mapval_kind; - struct hash_gciter map_iter; - struct hash_gciter_data d; Hchan *chan; ChanType *chantype; #endif @@ -663,8 +693,8 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) runtime_throw("scanblock: size of Workbuf is suboptimal"); // Memory arena parameters. - arena_start = runtime_mheap->arena_start; - arena_used = runtime_mheap->arena_used; + arena_start = runtime_mheap.arena_start; + arena_used = runtime_mheap.arena_used; stack_ptr = stack+nelem(stack)-1; @@ -685,10 +715,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) // (Silence the compiler) #if 0 - map_ret = nil; - mapkey_size = mapval_size = 0; - mapkey_kind = mapval_kind = false; - mapkey_ti = mapval_ti = 0; chan = nil; chantype = nil; chan_ret = nil; @@ -759,23 +785,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) stack_top.elemsize = pc[0]; stack_top.loop_or_ret = pc+1; break; - case TypeInfo_Map: - hmap = (Hmap*)b; - maptype = (MapType*)t; - if(hash_gciter_init(hmap, &map_iter)) { - mapkey_size = maptype->key->size; - mapkey_kind = maptype->key->kind; - mapkey_ti = (uintptr)maptype->key->gc | PRECISE; - mapval_size = maptype->elem->size; - mapval_kind = maptype->elem->kind; - mapval_ti = (uintptr)maptype->elem->gc | PRECISE; - - map_ret = nil; - pc = mapProg; - } else { - goto next_block; - } - break; case TypeInfo_Chan: chan = (Hchan*)b; chantype = (ChanType*)t; @@ -985,79 +994,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) pc = (uintptr*)((byte*)pc + *(int32*)(pc+2)); // target of the CALL instruction continue; -#if 0 - case GC_MAP_PTR: - hmap = *(Hmap**)(stack_top.b + pc[1]); - if(hmap == nil) { - pc += 3; - continue; - } - if(markonly(hmap)) { - maptype = (MapType*)pc[2]; - if(hash_gciter_init(hmap, &map_iter)) { - mapkey_size = maptype->key->size; - mapkey_kind = maptype->key->kind; - mapkey_ti = (uintptr)maptype->key->gc | PRECISE; - mapval_size = maptype->elem->size; - mapval_kind = maptype->elem->kind; - mapval_ti = (uintptr)maptype->elem->gc | PRECISE; - - // Start mapProg. - map_ret = pc+3; - pc = mapProg+1; - } else { - pc += 3; - } - } else { - pc += 3; - } - continue; - - case GC_MAP_NEXT: - // Add all keys and values to buffers, mark all subtables. - while(hash_gciter_next(&map_iter, &d)) { - // buffers: reserve space for 2 objects. - if(ptrbufpos+2 >= ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); - if(objbufpos+2 >= objbuf_end) - flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); - - if(d.st != nil) - markonly(d.st); - - if(d.key_data != nil) { - if(!(mapkey_kind & KindNoPointers) || d.indirectkey) { - if(!d.indirectkey) - *objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti}; - else { - if(Debug) { - obj = *(void**)d.key_data; - if(!(arena_start <= obj && obj < arena_used)) - runtime_throw("scanblock: inconsistent hashmap"); - } - *ptrbufpos++ = (struct PtrTarget){*(void**)d.key_data, mapkey_ti}; - } - } - if(!(mapval_kind & KindNoPointers) || d.indirectval) { - if(!d.indirectval) - *objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti}; - else { - if(Debug) { - obj = *(void**)d.val_data; - if(!(arena_start <= obj && obj < arena_used)) - runtime_throw("scanblock: inconsistent hashmap"); - } - *ptrbufpos++ = (struct PtrTarget){*(void**)d.val_data, mapval_ti}; - } - } - } - } - if(map_ret == nil) - goto next_block; - pc = map_ret; - continue; -#endif - case GC_REGION: obj = (void*)(stack_top.b + pc[1]); size = pc[2]; @@ -1071,7 +1007,6 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) #if 0 case GC_CHAN_PTR: - // Similar to GC_MAP_PTR chan = *(Hchan**)(stack_top.b + pc[1]); if(chan == nil) { pc += 3; @@ -1191,14 +1126,14 @@ debug_scanblock(byte *b, uintptr n) obj = (byte*)vp[i]; // Words outside the arena cannot be pointers. - if((byte*)obj < runtime_mheap->arena_start || (byte*)obj >= runtime_mheap->arena_used) + if((byte*)obj < runtime_mheap.arena_start || (byte*)obj >= runtime_mheap.arena_used) continue; // Round down to word boundary. obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1)); // Consult span table to find beginning. - s = runtime_MHeap_LookupMaybe(runtime_mheap, obj); + s = runtime_MHeap_LookupMaybe(&runtime_mheap, obj); if(s == nil) continue; @@ -1207,15 +1142,13 @@ debug_scanblock(byte *b, uintptr n) if(s->sizeclass == 0) { obj = p; } else { - if((byte*)obj >= (byte*)s->limit) - continue; int32 i = ((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; - bitp = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)obj - (uintptr*)runtime_mheap.arena_start; + bitp = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; xbits = *bitp; bits = xbits >> shift; @@ -1230,7 +1163,7 @@ debug_scanblock(byte *b, uintptr n) runtime_printf("found unmarked block %p in %p\n", obj, vp+i); // If object has no pointers, don't need to scan further. - if((bits & bitNoPointers) != 0) + if((bits & bitNoScan) != 0) continue; debug_scanblock(obj, size); @@ -1320,7 +1253,7 @@ getempty(Workbuf *b) runtime_lock(&work); if(work.nchunk < sizeof *b) { work.nchunk = 1<<20; - work.chunk = runtime_SysAlloc(work.nchunk); + work.chunk = runtime_SysAlloc(work.nchunk, &mstats.gc_sys); if(work.chunk == nil) runtime_throw("runtime: cannot allocate memory"); } @@ -1416,12 +1349,12 @@ addroot(Obj obj) cap = PageSize/sizeof(Obj); if(cap < 2*work.rootcap) cap = 2*work.rootcap; - new = (Obj*)runtime_SysAlloc(cap*sizeof(Obj)); + new = (Obj*)runtime_SysAlloc(cap*sizeof(Obj), &mstats.gc_sys); if(new == nil) runtime_throw("runtime: cannot allocate memory"); if(work.roots != nil) { runtime_memmove(new, work.roots, work.rootcap*sizeof(Obj)); - runtime_SysFree(work.roots, work.rootcap*sizeof(Obj)); + runtime_SysFree(work.roots, work.rootcap*sizeof(Obj), &mstats.gc_sys); } work.roots = new; work.rootcap = cap; @@ -1560,8 +1493,8 @@ addroots(void) runtime_time_scan(addroot); // MSpan.types - allspans = runtime_mheap->allspans; - for(spanidx=0; spanidx<runtime_mheap->nspan; spanidx++) { + 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: @@ -1589,10 +1522,7 @@ addroots(void) case Gdead: break; case Grunning: - if(gp != runtime_g()) - runtime_throw("mark - world not stopped"); - addstackroots(gp); - break; + runtime_throw("mark - world not stopped"); case Grunnable: case Gsyscall: case Gwaiting: @@ -1614,10 +1544,11 @@ handlespecial(byte *p, uintptr size) { FuncVal *fn; const struct __go_func_type *ft; + const struct __go_ptr_type *ot; FinBlock *block; Finalizer *f; - if(!runtime_getfinalizer(p, true, &fn, &ft)) { + if(!runtime_getfinalizer(p, true, &fn, &ft, &ot)) { runtime_setblockspecial(p, false); runtime_MProf_Free(p, size); return false; @@ -1626,9 +1557,7 @@ handlespecial(byte *p, uintptr size) runtime_lock(&finlock); if(finq == nil || finq->cnt == finq->cap) { if(finc == nil) { - finc = runtime_SysAlloc(PageSize); - if(finc == nil) - runtime_throw("runtime: cannot allocate memory"); + finc = runtime_persistentalloc(PageSize, 0, &mstats.gc_sys); finc->cap = (PageSize - sizeof(FinBlock)) / sizeof(Finalizer) + 1; finc->alllink = allfin; allfin = finc; @@ -1642,6 +1571,7 @@ handlespecial(byte *p, uintptr size) finq->cnt++; f->fn = fn; f->ft = ft; + f->ot = ot; f->arg = p; runtime_unlock(&finlock); return true; @@ -1668,10 +1598,10 @@ sweepspan(ParFor *desc, uint32 idx) m = runtime_m(); USED(&desc); - s = runtime_mheap->allspans[idx]; + s = runtime_mheap.allspans[idx]; if(s->state != MSpanInUse) return; - arena_start = runtime_mheap->arena_start; + arena_start = runtime_mheap.arena_start; p = (byte*)(s->start << PageShift); cl = s->sizeclass; size = s->elemsize; @@ -1735,9 +1665,9 @@ sweepspan(ParFor *desc, uint32 idx) // Free large span. runtime_unmarkspan(p, 1<<PageShift); *(uintptr*)p = (uintptr)0xdeaddeaddeaddeadll; // needs zeroing - runtime_MHeap_Free(runtime_mheap, s, 1); - c->local_alloc -= size; - c->local_nfree++; + runtime_MHeap_Free(&runtime_mheap, s, 1); + c->local_nlargefree++; + c->local_largefree += size; } else { // Free small object. switch(compression) { @@ -1758,12 +1688,9 @@ sweepspan(ParFor *desc, uint32 idx) } if(nfree) { - c->local_by_size[cl].nfree += nfree; - c->local_alloc -= size * nfree; - c->local_nfree += nfree; + c->local_nsmallfree[cl] += nfree; c->local_cachealloc -= nfree * size; - c->local_objects -= nfree; - runtime_MCentral_FreeSpan(&runtime_mheap->central[cl], s, nfree, head.next, end); + runtime_MCentral_FreeSpan(&runtime_mheap.central[cl], s, nfree, head.next, end); } } @@ -1777,10 +1704,10 @@ dumpspan(uint32 idx) MSpan *s; bool allocated, special; - s = runtime_mheap->allspans[idx]; + s = runtime_mheap.allspans[idx]; if(s->state != MSpanInUse) return; - arena_start = runtime_mheap->arena_start; + arena_start = runtime_mheap.arena_start; p = (byte*)(s->start << PageShift); sizeclass = s->sizeclass; size = s->elemsize; @@ -1838,7 +1765,7 @@ runtime_memorydump(void) { uint32 spanidx; - for(spanidx=0; spanidx<runtime_mheap->nspan; spanidx++) { + for(spanidx=0; spanidx<runtime_mheap.nspan; spanidx++) { dumpspan(spanidx); } } @@ -1880,13 +1807,28 @@ runtime_gchelper(void) static int32 gcpercent = GcpercentUnknown; static void -cachestats(GCStats *stats) +cachestats(void) +{ + MCache *c; + P *p, **pp; + + for(pp=runtime_allp; (p=*pp) != nil; pp++) { + c = p->mcache; + if(c==nil) + continue; + runtime_purgecachedstats(c); + } +} + +static void +updatememstats(GCStats *stats) { M *mp; + MSpan *s; MCache *c; P *p, **pp; uint32 i; - uint64 stacks_inuse; + uint64 stacks_inuse, smallfree; uint64 *src, *dst; if(stats) @@ -1902,29 +1844,80 @@ cachestats(GCStats *stats) runtime_memclr((byte*)&mp->gcstats, sizeof(mp->gcstats)); } } + mstats.stacks_inuse = stacks_inuse; + mstats.mcache_inuse = runtime_mheap.cachealloc.inuse; + mstats.mspan_inuse = runtime_mheap.spanalloc.inuse; + mstats.sys = mstats.heap_sys + mstats.stacks_sys + mstats.mspan_sys + + mstats.mcache_sys + mstats.buckhash_sys + mstats.gc_sys + mstats.other_sys; + + // Calculate memory allocator stats. + // During program execution we only count number of frees and amount of freed memory. + // Current number of alive object in the heap and amount of alive heap memory + // are calculated by scanning all spans. + // Total number of mallocs is calculated as number of frees plus number of alive objects. + // Similarly, total amount of allocated memory is calculated as amount of freed memory + // plus amount of alive heap memory. + mstats.alloc = 0; + mstats.total_alloc = 0; + mstats.nmalloc = 0; + mstats.nfree = 0; + for(i = 0; i < nelem(mstats.by_size); i++) { + mstats.by_size[i].nmalloc = 0; + mstats.by_size[i].nfree = 0; + } + + // Flush MCache's to MCentral. for(pp=runtime_allp; (p=*pp) != nil; pp++) { c = p->mcache; if(c==nil) continue; - runtime_purgecachedstats(c); - for(i=0; i<nelem(c->local_by_size); i++) { - mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc; - c->local_by_size[i].nmalloc = 0; - mstats.by_size[i].nfree += c->local_by_size[i].nfree; - c->local_by_size[i].nfree = 0; + runtime_MCache_ReleaseAll(c); + } + + // Aggregate local stats. + cachestats(); + + // Scan all spans and count number of alive objects. + for(i = 0; i < runtime_mheap.nspan; i++) { + s = runtime_mheap.allspans[i]; + if(s->state != MSpanInUse) + continue; + if(s->sizeclass == 0) { + mstats.nmalloc++; + mstats.alloc += s->elemsize; + } else { + mstats.nmalloc += s->ref; + mstats.by_size[s->sizeclass].nmalloc += s->ref; + mstats.alloc += s->ref*s->elemsize; } } - mstats.stacks_inuse = stacks_inuse; + + // Aggregate by size class. + smallfree = 0; + mstats.nfree = runtime_mheap.nlargefree; + for(i = 0; i < nelem(mstats.by_size); i++) { + mstats.nfree += runtime_mheap.nsmallfree[i]; + mstats.by_size[i].nfree = runtime_mheap.nsmallfree[i]; + mstats.by_size[i].nmalloc += runtime_mheap.nsmallfree[i]; + smallfree += runtime_mheap.nsmallfree[i] * runtime_class_to_size[i]; + } + mstats.nmalloc += mstats.nfree; + + // Calculate derived stats. + mstats.total_alloc = mstats.alloc + runtime_mheap.largefree + smallfree; + mstats.heap_alloc = mstats.alloc; + mstats.heap_objects = mstats.nmalloc - mstats.nfree; } // Structure of arguments passed to function gc(). -// This allows the arguments to be passed via reflect_call. +// This allows the arguments to be passed via runtime_mcall. struct gc_args { - int32 force; + int64 start_time; // start time of GC in ns (just before stoptheworld) }; static void gc(struct gc_args *args); +static void mgc(G *gp); static int32 readgogc(void) @@ -1943,8 +1936,9 @@ void runtime_gc(int32 force) { M *m; - const byte *p; - struct gc_args a, *ap; + G *g; + struct gc_args a; + int32 i; // The atomic operations are not atomic if the uint64s // are not aligned on uint64 boundaries. This has been @@ -1967,30 +1961,77 @@ runtime_gc(int32 force) // while holding a lock. The next mallocgc // without a lock will do the gc instead. m = runtime_m(); - if(!mstats.enablegc || m->locks > 0 || runtime_panicking) + if(!mstats.enablegc || runtime_g() == m->g0 || m->locks > 0 || runtime_panicking) return; if(gcpercent == GcpercentUnknown) { // first time through - gcpercent = readgogc(); - - p = runtime_getenv("GOGCTRACE"); - if(p != nil) - gctrace = runtime_atoi(p); + runtime_lock(&runtime_mheap); + if(gcpercent == GcpercentUnknown) + gcpercent = readgogc(); + runtime_unlock(&runtime_mheap); } if(gcpercent < 0) return; - // Run gc on a bigger stack to eliminate - // a potentially large number of calls to runtime_morestack. - // But not when using gccgo. - a.force = force; - ap = &a; - gc(ap); + runtime_semacquire(&runtime_worldsema, false); + if(!force && 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); + return; + } - if(gctrace > 1 && !force) { - a.force = 1; - gc(&a); + // Ok, we're doing it! Stop everybody else + a.start_time = runtime_nanotime(); + m->gcing = 1; + runtime_stoptheworld(); + + // Run gc on the g0 stack. We do this so that the g stack + // we're currently running on will no longer change. Cuts + // the root set down a bit (g0 stacks are not scanned, and + // 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++) { + // 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 + m->gcing = 0; + m->locks++; + runtime_semrelease(&runtime_worldsema); + runtime_starttheworld(); + m->locks--; + + // now that gc is done, kick off finalizer thread if needed + if(finq != nil) { + runtime_lock(&finlock); + // 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(&finlock); + } + // give the queued finalizers, if any, a chance to run + runtime_gosched(); +} + +static void +mgc(G *gp) +{ + gc(gp->param); + gp->param = nil; + gp->status = Grunning; + runtime_gogo(gp); } static void @@ -2004,29 +2045,20 @@ gc(struct gc_args *args) uint32 i; // Eface eface; - runtime_semacquire(&runtime_worldsema); - if(!args->force && mstats.heap_alloc < mstats.next_gc) { - runtime_semrelease(&runtime_worldsema); - return; - } - m = runtime_m(); - t0 = runtime_nanotime(); - - m->gcing = 1; - runtime_stoptheworld(); + t0 = args->start_time; if(CollectStats) runtime_memclr((byte*)&gcstats, sizeof(gcstats)); for(mp=runtime_allm; mp; mp=mp->alllink) - runtime_settype_flush(mp, false); + runtime_settype_flush(mp); heap0 = 0; obj0 = 0; - if(gctrace) { - cachestats(nil); + if(runtime_debug.gctrace) { + updatememstats(nil); heap0 = mstats.heap_alloc; obj0 = mstats.nmalloc - mstats.nfree; } @@ -2050,7 +2082,7 @@ gc(struct gc_args *args) work.nproc = runtime_gcprocs(); addroots(); runtime_parforsetup(work.markfor, work.nproc, work.nroot, nil, false, markroot); - runtime_parforsetup(work.sweepfor, work.nproc, runtime_mheap->nspan, nil, true, sweepspan); + runtime_parforsetup(work.sweepfor, work.nproc, runtime_mheap.nspan, nil, true, sweepspan); if(work.nproc > 1) { runtime_noteclear(&work.alldone); runtime_helpgc(work.nproc); @@ -2076,29 +2108,8 @@ gc(struct gc_args *args) if(work.nproc > 1) runtime_notesleep(&work.alldone); - cachestats(&stats); - - stats.nprocyield += work.sweepfor->nprocyield; - stats.nosyield += work.sweepfor->nosyield; - stats.nsleep += work.sweepfor->nsleep; - - mstats.next_gc = mstats.heap_alloc+(mstats.heap_alloc-runtime_stacks_sys)*gcpercent/100; - m->gcing = 0; - - if(finq != nil) { - m->locks++; // disable gc during the mallocs in newproc - // 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); - } - m->locks--; - } - - heap1 = mstats.heap_alloc; - obj1 = mstats.nmalloc - mstats.nfree; + cachestats(); + mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100; t4 = runtime_nanotime(); mstats.last_gc = t4; @@ -2108,7 +2119,15 @@ gc(struct gc_args *args) if(mstats.debuggc) runtime_printf("pause %D\n", t4-t0); - if(gctrace) { + if(runtime_debug.gctrace) { + updatememstats(&stats); + heap1 = mstats.heap_alloc; + obj1 = mstats.nmalloc - mstats.nfree; + + stats.nprocyield += work.sweepfor->nprocyield; + stats.nosyield += work.sweepfor->nosyield; + stats.nsleep += work.sweepfor->nsleep; + runtime_printf("gc%d(%d): %D+%D+%D ms, %D -> %D MB %D -> %D (%D-%D) objects," " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n", mstats.numgc, work.nproc, (t2-t1)/1000000, (t3-t2)/1000000, (t1-t0+t4-t3)/1000000, @@ -2137,16 +2156,13 @@ gc(struct gc_args *args) runtime_printf("\ttotal:\t%D\n", ninstr); runtime_printf("putempty: %D, getfull: %D\n", gcstats.putempty, gcstats.getfull); + + runtime_printf("markonly base lookup: bit %D word %D span %D\n", gcstats.markonly.foundbit, gcstats.markonly.foundword, gcstats.markonly.foundspan); + runtime_printf("flushptrbuf base lookup: bit %D word %D span %D\n", gcstats.flushptrbuf.foundbit, gcstats.flushptrbuf.foundword, gcstats.flushptrbuf.foundspan); } } runtime_MProf_GC(); - runtime_semrelease(&runtime_worldsema); - runtime_starttheworld(); - - // give the queued finalizers, if any, a chance to run - if(finq != nil) - runtime_gosched(); } void runtime_ReadMemStats(MStats *) @@ -2161,15 +2177,17 @@ runtime_ReadMemStats(MStats *stats) // because stoptheworld can only be used by // one goroutine at a time, and there might be // a pending garbage collection already calling it. - runtime_semacquire(&runtime_worldsema); + runtime_semacquire(&runtime_worldsema, false); m = runtime_m(); m->gcing = 1; runtime_stoptheworld(); - cachestats(nil); + updatememstats(nil); *stats = mstats; m->gcing = 0; + m->locks++; runtime_semrelease(&runtime_worldsema); runtime_starttheworld(); + m->locks--; } void runtime_debug_readGCStats(Slice*) @@ -2187,7 +2205,7 @@ runtime_debug_readGCStats(Slice *pauses) // Pass back: pauses, last gc (absolute time), number of gc, total pause ns. p = (uint64*)pauses->array; - runtime_lock(runtime_mheap); + runtime_lock(&runtime_mheap); n = mstats.numgc; if(n > nelem(mstats.pause_ns)) n = nelem(mstats.pause_ns); @@ -2202,7 +2220,7 @@ runtime_debug_readGCStats(Slice *pauses) p[n] = mstats.last_gc; p[n+1] = mstats.numgc; p[n+2] = mstats.pause_total_ns; - runtime_unlock(runtime_mheap); + runtime_unlock(&runtime_mheap); pauses->__count = n+3; } @@ -2214,14 +2232,14 @@ runtime_debug_setGCPercent(intgo in) { intgo out; - runtime_lock(runtime_mheap); + runtime_lock(&runtime_mheap); if(gcpercent == GcpercentUnknown) gcpercent = readgogc(); out = gcpercent; if(in < 0) in = -1; gcpercent = in; - runtime_unlock(runtime_mheap); + runtime_unlock(&runtime_mheap); return out; } @@ -2235,6 +2253,8 @@ gchelperstart(void) runtime_throw("gchelperstart: bad m->helpgc"); if(runtime_xchg(&bufferList[m->helpgc].busy, 1)) runtime_throw("gchelperstart: already busy"); + if(runtime_g() != m->g0) + runtime_throw("gchelper not running on g0 stack"); } static void @@ -2243,33 +2263,51 @@ runfinq(void* dummy __attribute__ ((unused))) Finalizer *f; FinBlock *fb, *next; uint32 i; + Eface ef; + Iface iface; for(;;) { - // There's no need for a lock in this section - // because it only conflicts with the garbage - // collector, and the garbage collector only - // runs when everyone else is stopped, and - // runfinq only stops at the gosched() or - // during the calls in the for loop. + runtime_lock(&finlock); fb = finq; finq = nil; if(fb == nil) { fingwait = 1; - runtime_park(nil, nil, "finalizer wait"); + runtime_park(runtime_unlock, &finlock, "finalizer wait"); continue; } + runtime_unlock(&finlock); if(raceenabled) runtime_racefingo(); for(; fb; fb=next) { next = fb->next; for(i=0; i<(uint32)fb->cnt; i++) { + const Type *fint; void *param; f = &fb->fin[i]; - param = &f->arg; + fint = ((const Type**)f->ft->__in.array)[0]; + if(fint->kind == KindPtr) { + // direct use of pointer + param = &f->arg; + } else if(((const InterfaceType*)fint)->__methods.__count == 0) { + // convert to empty interface + ef.type = (const Type*)f->ot; + ef.__object = f->arg; + param = &ef; + } else { + // convert to interface with methods + iface.__methods = __go_convert_interface_2((const Type*)fint, + (const Type*)f->ot, + 1); + iface.__object = f->arg; + if(iface.__methods == nil) + runtime_throw("invalid type conversion in runfinq"); + param = &iface; + } reflect_call(f->ft, f->fn, 0, 0, ¶m, nil); f->fn = nil; f->arg = nil; + f->ot = nil; } fb->cnt = 0; fb->next = finc; @@ -2280,28 +2318,28 @@ runfinq(void* dummy __attribute__ ((unused))) } // mark the block at v of size n as allocated. -// If noptr is true, mark it as having no pointers. +// If noscan is true, mark it as not needing scanning. void -runtime_markallocated(void *v, uintptr n, bool noptr) +runtime_markallocated(void *v, uintptr n, bool noscan) { uintptr *b, obits, bits, off, shift; if(0) runtime_printf("markallocated %p+%p\n", v, n); - if((byte*)v+n > (byte*)runtime_mheap->arena_used || (byte*)v < runtime_mheap->arena_start) + if((byte*)v+n > (byte*)runtime_mheap.arena_used || (byte*)v < runtime_mheap.arena_start) runtime_throw("markallocated: bad pointer"); - off = (uintptr*)v - (uintptr*)runtime_mheap->arena_start; // word offset - b = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + 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; bits = (obits & ~(bitMask<<shift)) | (bitAllocated<<shift); - if(noptr) - bits |= bitNoPointers<<shift; - if(runtime_singleproc) { + if(noscan) + bits |= bitNoScan<<shift; + if(runtime_gomaxprocs == 1) { *b = bits; break; } else { @@ -2319,19 +2357,19 @@ runtime_markfreed(void *v, uintptr n) uintptr *b, obits, bits, off, shift; if(0) - runtime_printf("markallocated %p+%p\n", v, n); + runtime_printf("markfreed %p+%p\n", v, n); - if((byte*)v+n > (byte*)runtime_mheap->arena_used || (byte*)v < runtime_mheap->arena_start) - runtime_throw("markallocated: bad pointer"); + if((byte*)v+n > (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; + 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; bits = (obits & ~(bitMask<<shift)) | (bitBlockBoundary<<shift); - if(runtime_singleproc) { + if(runtime_gomaxprocs == 1) { *b = bits; break; } else { @@ -2351,11 +2389,11 @@ runtime_checkfreed(void *v, uintptr n) if(!runtime_checking) return; - if((byte*)v+n > (byte*)runtime_mheap->arena_used || (byte*)v < runtime_mheap->arena_start) + if((byte*)v+n > (byte*)runtime_mheap.arena_used || (byte*)v < runtime_mheap.arena_start) return; // not allocated, so okay - off = (uintptr*)v - (uintptr*)runtime_mheap->arena_start; // word offset - b = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)v - (uintptr*)runtime_mheap.arena_start; // word offset + b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; bits = *b>>shift; @@ -2374,7 +2412,7 @@ runtime_markspan(void *v, uintptr size, uintptr n, bool leftover) uintptr *b, off, shift; byte *p; - if((byte*)v+size*n > (byte*)runtime_mheap->arena_used || (byte*)v < runtime_mheap->arena_start) + if((byte*)v+size*n > (byte*)runtime_mheap.arena_used || (byte*)v < runtime_mheap.arena_start) runtime_throw("markspan: bad pointer"); p = v; @@ -2385,8 +2423,8 @@ runtime_markspan(void *v, uintptr size, uintptr n, bool leftover) // the entire span, and each bitmap word has bits for only // one span, so no other goroutines are changing these // bitmap words. - off = (uintptr*)p - (uintptr*)runtime_mheap->arena_start; // word offset - b = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + 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)) | (bitBlockBoundary<<shift); } @@ -2398,14 +2436,14 @@ runtime_unmarkspan(void *v, uintptr n) { uintptr *p, *b, off; - if((byte*)v+n > (byte*)runtime_mheap->arena_used || (byte*)v < runtime_mheap->arena_start) + if((byte*)v+n > (byte*)runtime_mheap.arena_used || (byte*)v < runtime_mheap.arena_start) runtime_throw("markspan: bad pointer"); p = v; - off = p - (uintptr*)runtime_mheap->arena_start; // word offset + off = p - (uintptr*)runtime_mheap.arena_start; // word offset if(off % wordsPerBitmapWord != 0) runtime_throw("markspan: unaligned pointer"); - b = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1; n /= PtrSize; if(n%wordsPerBitmapWord != 0) runtime_throw("unmarkspan: unaligned length"); @@ -2426,8 +2464,8 @@ runtime_blockspecial(void *v) if(DebugMark) return true; - off = (uintptr*)v - (uintptr*)runtime_mheap->arena_start; - b = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)v - (uintptr*)runtime_mheap.arena_start; + b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; return (*b & (bitSpecial<<shift)) != 0; @@ -2441,8 +2479,8 @@ runtime_setblockspecial(void *v, bool s) if(DebugMark) return; - off = (uintptr*)v - (uintptr*)runtime_mheap->arena_start; - b = (uintptr*)runtime_mheap->arena_start - off/wordsPerBitmapWord - 1; + off = (uintptr*)v - (uintptr*)runtime_mheap.arena_start; + b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1; shift = off % wordsPerBitmapWord; for(;;) { @@ -2451,7 +2489,7 @@ runtime_setblockspecial(void *v, bool s) bits = obits | (bitSpecial<<shift); else bits = obits & ~(bitSpecial<<shift); - if(runtime_singleproc) { + if(runtime_gomaxprocs == 1) { *b = bits; break; } else { @@ -2476,13 +2514,13 @@ runtime_MHeap_MapBits(MHeap *h) uintptr n; n = (h->arena_used - h->arena_start) / wordsPerBitmapWord; - n = (n+bitmapChunk-1) & ~(bitmapChunk-1); + n = ROUND(n, bitmapChunk); 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); + runtime_SysMap(h->arena_start - n, n - h->bitmap_mapped, &mstats.gc_sys); h->bitmap_mapped = n; } diff --git a/libgo/runtime/mgc0.h b/libgo/runtime/mgc0.h index d14fb37..f8abe6c 100644 --- a/libgo/runtime/mgc0.h +++ b/libgo/runtime/mgc0.h @@ -26,7 +26,6 @@ enum { GC_ARRAY_START, // Start an array with a fixed length. Args: (off, len, elemsize) GC_ARRAY_NEXT, // The next element of an array. Args: none GC_CALL, // Call a subroutine. Args: (off, objgcrel) - GC_MAP_PTR, // Go map. Args: (off, MapType*) GC_CHAN_PTR, // Go channel. Args: (off, ChanType*) GC_STRING, // Go string. Args: (off) GC_EFACE, // interface{}. Args: (off) diff --git a/libgo/runtime/mheap.c b/libgo/runtime/mheap.c index b4d94b6..1b6cfd3 100644 --- a/libgo/runtime/mheap.c +++ b/libgo/runtime/mheap.c @@ -36,12 +36,12 @@ RecordSpan(void *vh, byte *p) cap = 64*1024/sizeof(all[0]); if(cap < h->nspancap*3/2) cap = h->nspancap*3/2; - all = (MSpan**)runtime_SysAlloc(cap*sizeof(all[0])); + all = (MSpan**)runtime_SysAlloc(cap*sizeof(all[0]), &mstats.other_sys); if(all == nil) runtime_throw("runtime: cannot allocate memory"); if(h->allspans) { runtime_memmove(all, h->allspans, h->nspancap*sizeof(all[0])); - runtime_SysFree(h->allspans, h->nspancap*sizeof(all[0])); + runtime_SysFree(h->allspans, h->nspancap*sizeof(all[0]), &mstats.other_sys); } h->allspans = all; h->nspancap = cap; @@ -51,12 +51,12 @@ RecordSpan(void *vh, byte *p) // Initialize the heap; fetch memory using alloc. void -runtime_MHeap_Init(MHeap *h, void *(*alloc)(uintptr)) +runtime_MHeap_Init(MHeap *h) { uint32 i; - runtime_FixAlloc_Init(&h->spanalloc, sizeof(MSpan), alloc, RecordSpan, h); - runtime_FixAlloc_Init(&h->cachealloc, sizeof(MCache), alloc, nil, nil); + runtime_FixAlloc_Init(&h->spanalloc, sizeof(MSpan), RecordSpan, h, &mstats.mspan_sys); + runtime_FixAlloc_Init(&h->cachealloc, sizeof(MCache), nil, nil, &mstats.mcache_sys); // h->mapcache needs no init for(i=0; i<nelem(h->free); i++) runtime_MSpanList_Init(&h->free[i]); @@ -65,6 +65,23 @@ runtime_MHeap_Init(MHeap *h, void *(*alloc)(uintptr)) runtime_MCentral_Init(&h->central[i], i); } +void +runtime_MHeap_MapSpans(MHeap *h) +{ + uintptr n; + + // Map spans array, PageSize at a time. + n = (uintptr)h->arena_used; + if(sizeof(void*) == 8) + n -= (uintptr)h->arena_start; + n = n / PageSize * sizeof(h->spans[0]); + 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); + h->spans_mapped = n; +} + // Allocate a new span of npage pages from the heap // and record its size class in the HeapMap and HeapMapCache. MSpan* @@ -73,7 +90,8 @@ runtime_MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass, int32 acct, int32 MSpan *s; runtime_lock(h); - runtime_purgecachedstats(runtime_m()->mcache); + mstats.heap_alloc += runtime_m()->mcache->local_cachealloc; + runtime_m()->mcache->local_cachealloc = 0; s = MHeap_AllocLocked(h, npage, sizeclass); if(s != nil) { mstats.heap_inuse += npage<<PageShift; @@ -138,6 +156,7 @@ HaveSpan: // is just a unique constant not seen elsewhere in the // runtime, as a clue in case it turns up unexpectedly in // memory or in a stack trace. + runtime_SysUsed((void*)(s->start<<PageShift), s->npages<<PageShift); *(uintptr*)(s->start<<PageShift) = (uintptr)0xbeadbeadbeadbeadULL; } s->npreleased = 0; @@ -145,17 +164,15 @@ HaveSpan: if(s->npages > npage) { // Trim extra and put it back in the heap. t = runtime_FixAlloc_Alloc(&h->spanalloc); - mstats.mspan_inuse = h->spanalloc.inuse; - mstats.mspan_sys = h->spanalloc.sys; runtime_MSpan_Init(t, s->start + npage, s->npages - npage); s->npages = npage; p = t->start; if(sizeof(void*) == 8) p -= ((uintptr)h->arena_start>>PageShift); if(p > 0) - h->map[p-1] = s; - h->map[p] = t; - h->map[p+t->npages-1] = t; + h->spans[p-1] = s; + h->spans[p] = t; + h->spans[p+t->npages-1] = t; *(uintptr*)(t->start<<PageShift) = *(uintptr*)(s->start<<PageShift); // copy "needs zeroing" mark t->state = MSpanInUse; MHeap_FreeLocked(h, t); @@ -172,7 +189,7 @@ HaveSpan: if(sizeof(void*) == 8) p -= ((uintptr)h->arena_start>>PageShift); for(n=0; n<npage; n++) - h->map[p+n] = s; + h->spans[p+n] = s; return s; } @@ -232,19 +249,16 @@ MHeap_Grow(MHeap *h, uintptr npage) return false; } } - mstats.heap_sys += ask; // Create a fake "in use" span and free it, so that the // right coalescing happens. s = runtime_FixAlloc_Alloc(&h->spanalloc); - mstats.mspan_inuse = h->spanalloc.inuse; - mstats.mspan_sys = h->spanalloc.sys; runtime_MSpan_Init(s, (uintptr)v>>PageShift, ask>>PageShift); p = s->start; if(sizeof(void*) == 8) p -= ((uintptr)h->arena_start>>PageShift); - h->map[p] = s; - h->map[p + s->npages - 1] = s; + h->spans[p] = s; + h->spans[p + s->npages - 1] = s; s->state = MSpanInUse; MHeap_FreeLocked(h, s); return true; @@ -261,7 +275,7 @@ runtime_MHeap_Lookup(MHeap *h, void *v) p = (uintptr)v; if(sizeof(void*) == 8) p -= (uintptr)h->arena_start; - return h->map[p >> PageShift]; + return h->spans[p >> PageShift]; } // Look up the span at the given address. @@ -283,10 +297,8 @@ runtime_MHeap_LookupMaybe(MHeap *h, void *v) q = p; if(sizeof(void*) == 8) q -= (uintptr)h->arena_start >> PageShift; - s = h->map[q]; - if(s == nil || p < s->start || p - s->start >= s->npages) - return nil; - if(s->state != MSpanInUse) + s = h->spans[q]; + if(s == nil || p < s->start || (byte*)v >= s->limit || s->state != MSpanInUse) return nil; return s; } @@ -296,7 +308,8 @@ void runtime_MHeap_Free(MHeap *h, MSpan *s, int32 acct) { runtime_lock(h); - runtime_purgecachedstats(runtime_m()->mcache); + mstats.heap_alloc += runtime_m()->mcache->local_cachealloc; + runtime_m()->mcache->local_cachealloc = 0; mstats.heap_inuse -= s->npages<<PageShift; if(acct) { mstats.heap_alloc -= s->npages<<PageShift; @@ -313,8 +326,6 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) MSpan *t; PageID p; - if(s->types.sysalloc) - runtime_settype_sysfree(s); s->types.compression = MTypes_Empty; if(s->state != MSpanInUse || s->ref != 0) { @@ -334,31 +345,31 @@ MHeap_FreeLocked(MHeap *h, MSpan *s) p = s->start; if(sizeof(void*) == 8) p -= (uintptr)h->arena_start >> PageShift; - if(p > 0 && (t = h->map[p-1]) != nil && t->state != MSpanInUse) { - tp = (uintptr*)(t->start<<PageShift); - *tp |= *sp; // propagate "needs zeroing" mark + if(p > 0 && (t = h->spans[p-1]) != nil && t->state != MSpanInUse) { + if(t->npreleased == 0) { // cant't touch this otherwise + tp = (uintptr*)(t->start<<PageShift); + *tp |= *sp; // propagate "needs zeroing" mark + } s->start = t->start; s->npages += t->npages; s->npreleased = t->npreleased; // absorb released pages p -= t->npages; - h->map[p] = s; + h->spans[p] = s; runtime_MSpanList_Remove(t); t->state = MSpanDead; runtime_FixAlloc_Free(&h->spanalloc, t); - mstats.mspan_inuse = h->spanalloc.inuse; - mstats.mspan_sys = h->spanalloc.sys; } - if(p+s->npages < nelem(h->map) && (t = h->map[p+s->npages]) != nil && t->state != MSpanInUse) { - tp = (uintptr*)(t->start<<PageShift); - *sp |= *tp; // propagate "needs zeroing" mark + if((p+s->npages)*sizeof(h->spans[0]) < h->spans_mapped && (t = h->spans[p+s->npages]) != nil && t->state != MSpanInUse) { + if(t->npreleased == 0) { // cant't touch this otherwise + tp = (uintptr*)(t->start<<PageShift); + *sp |= *tp; // propagate "needs zeroing" mark + } s->npages += t->npages; s->npreleased += t->npreleased; - h->map[p + s->npages - 1] = s; + h->spans[p + s->npages - 1] = s; runtime_MSpanList_Remove(t); t->state = MSpanDead; runtime_FixAlloc_Free(&h->spanalloc, t); - mstats.mspan_inuse = h->spanalloc.inuse; - mstats.mspan_sys = h->spanalloc.sys; } // Insert s into appropriate list. @@ -388,7 +399,7 @@ scavengelist(MSpan *list, uint64 now, uint64 limit) sumreleased = 0; for(s=list->next; s != list; s=s->next) { - if((now - s->unusedsince) > limit) { + if((now - s->unusedsince) > limit && s->npreleased != s->npages) { released = (s->npages - s->npreleased) << PageShift; mstats.heap_released += released; sumreleased += released; @@ -399,19 +410,26 @@ scavengelist(MSpan *list, uint64 now, uint64 limit) return sumreleased; } -static uintptr -scavenge(uint64 now, uint64 limit) +static void +scavenge(int32 k, uint64 now, uint64 limit) { uint32 i; uintptr sumreleased; MHeap *h; - h = runtime_mheap; + h = &runtime_mheap; sumreleased = 0; for(i=0; i < nelem(h->free); i++) sumreleased += scavengelist(&h->free[i], now, limit); sumreleased += scavengelist(&h->large, now, limit); - return sumreleased; + + if(runtime_debug.gctrace > 0) { + if(sumreleased > 0) + runtime_printf("scvg%d: %D MB released\n", k, (uint64)sumreleased>>20); + runtime_printf("scvg%d: inuse: %D, idle: %D, sys: %D, released: %D, consumed: %D (MB)\n", + k, mstats.heap_inuse>>20, mstats.heap_idle>>20, mstats.heap_sys>>20, + mstats.heap_released>>20, (mstats.heap_sys - mstats.heap_released)>>20); + } } // Release (part of) unused memory to OS. @@ -424,9 +442,6 @@ runtime_MHeap_Scavenger(void* dummy) MHeap *h; uint64 tick, now, forcegc, limit; uint32 k; - uintptr sumreleased; - const byte *env; - bool trace; Note note, *notep; USED(dummy); @@ -446,17 +461,10 @@ runtime_MHeap_Scavenger(void* dummy) else tick = limit/2; - trace = false; - env = runtime_getenv("GOGCTRACE"); - if(env != nil) - trace = runtime_atoi(env) > 0; - - h = runtime_mheap; + h = &runtime_mheap; for(k=0;; k++) { runtime_noteclear(¬e); - runtime_entersyscallblock(); - runtime_notetsleep(¬e, tick); - runtime_exitsyscall(); + runtime_notetsleepg(¬e, tick); runtime_lock(h); now = runtime_nanotime(); @@ -468,24 +476,14 @@ runtime_MHeap_Scavenger(void* dummy) runtime_noteclear(¬e); notep = ¬e; __go_go(forcegchelper, (void*)notep); - runtime_entersyscallblock(); - runtime_notesleep(¬e); - runtime_exitsyscall(); - if(trace) + runtime_notetsleepg(¬e, -1); + if(runtime_debug.gctrace > 0) runtime_printf("scvg%d: GC forced\n", k); runtime_lock(h); now = runtime_nanotime(); } - sumreleased = scavenge(now, limit); + scavenge(k, now, limit); runtime_unlock(h); - - if(trace) { - if(sumreleased > 0) - runtime_printf("scvg%d: %p MB released\n", k, sumreleased>>20); - runtime_printf("scvg%d: inuse: %D, idle: %D, sys: %D, released: %D, consumed: %D (MB)\n", - k, mstats.heap_inuse>>20, mstats.heap_idle>>20, mstats.heap_sys>>20, - mstats.heap_released>>20, (mstats.heap_sys - mstats.heap_released)>>20); - } } } @@ -495,9 +493,9 @@ void runtime_debug_freeOSMemory(void) { runtime_gc(1); - runtime_lock(runtime_mheap); - scavenge(~(uintptr)0, 0); - runtime_unlock(runtime_mheap); + runtime_lock(&runtime_mheap); + scavenge(-1, ~(uintptr)0, 0); + runtime_unlock(&runtime_mheap); } // Initialize a new span with the given start and npages. diff --git a/libgo/runtime/mprof.goc b/libgo/runtime/mprof.goc index 73d9379..7507dfc 100644 --- a/libgo/runtime/mprof.goc +++ b/libgo/runtime/mprof.goc @@ -14,44 +14,11 @@ package runtime #include "go-string.h" // NOTE(rsc): Everything here could use cas if contention became an issue. -static Lock proflock, alloclock; +static Lock proflock; // All memory allocations are local and do not escape outside of the profiler. // The profiler is forbidden from referring to garbage-collected memory. -static byte *pool; // memory allocation pool -static uintptr poolfree; // number of bytes left in the pool -enum { - Chunk = 32*PageSize, // initial size of the pool -}; - -// Memory allocation local to this file. -// There is no way to return the allocated memory back to the OS. -static void* -allocate(uintptr size) -{ - void *v; - - if(size == 0) - return nil; - - if(size >= Chunk/2) - return runtime_SysAlloc(size); - - runtime_lock(&alloclock); - if(size > poolfree) { - pool = runtime_SysAlloc(Chunk); - if(pool == nil) - runtime_throw("runtime: cannot allocate memory"); - poolfree = Chunk; - } - v = pool; - pool += size; - poolfree -= size; - runtime_unlock(&alloclock); - return v; -} - enum { MProf, BProf }; // profile types // Per-call-stack profiling information. @@ -104,10 +71,9 @@ stkbucket(int32 typ, Location *stk, int32 nstk, bool alloc) Bucket *b; if(buckhash == nil) { - buckhash = runtime_SysAlloc(BuckHashSize*sizeof buckhash[0]); + buckhash = runtime_SysAlloc(BuckHashSize*sizeof buckhash[0], &mstats.buckhash_sys); if(buckhash == nil) runtime_throw("runtime: cannot allocate memory"); - mstats.buckhash_sys += BuckHashSize*sizeof buckhash[0]; } // Hash stack. @@ -137,9 +103,7 @@ stkbucket(int32 typ, Location *stk, int32 nstk, bool alloc) if(!alloc) return nil; - b = allocate(sizeof *b + nstk*sizeof stk[0]); - if(b == nil) - runtime_throw("runtime: cannot allocate memory"); + b = runtime_persistentalloc(sizeof *b + nstk*sizeof stk[0], 0, &mstats.buckhash_sys); bucketmem += sizeof *b + nstk*sizeof stk[0]; runtime_memmove(b->stk, stk, nstk*sizeof stk[0]); b->typ = typ; @@ -241,7 +205,7 @@ setaddrbucket(uintptr addr, Bucket *b) if(ah->addr == (addr>>AddrHashShift)) goto found; - ah = allocate(sizeof *ah); + ah = runtime_persistentalloc(sizeof *ah, 0, &mstats.buckhash_sys); addrmem += sizeof *ah; ah->next = addrhash[h]; ah->addr = addr>>AddrHashShift; @@ -249,7 +213,7 @@ setaddrbucket(uintptr addr, Bucket *b) found: if((e = addrfree) == nil) { - e = allocate(64*sizeof *e); + e = runtime_persistentalloc(64*sizeof *e, 0, &mstats.buckhash_sys); addrmem += 64*sizeof *e; for(i=0; i+1<64; i++) e[i].next = &e[i+1]; @@ -296,16 +260,10 @@ found: void runtime_MProf_Malloc(void *p, uintptr size) { - M *m; int32 nstk; Location stk[32]; Bucket *b; - m = runtime_m(); - if(m->nomemprof > 0) - return; - - m->nomemprof++; nstk = runtime_callers(1, stk, 32); runtime_lock(&proflock); b = stkbucket(MProf, stk, nstk, true); @@ -313,22 +271,14 @@ runtime_MProf_Malloc(void *p, uintptr size) b->recent_alloc_bytes += size; setaddrbucket((uintptr)p, b); runtime_unlock(&proflock); - m = runtime_m(); - m->nomemprof--; } // Called when freeing a profiled block. void runtime_MProf_Free(void *p, uintptr size) { - M *m; Bucket *b; - m = runtime_m(); - if(m->nomemprof > 0) - return; - - m->nomemprof++; runtime_lock(&proflock); b = getaddrbucket((uintptr)p); if(b != nil) { @@ -336,8 +286,6 @@ runtime_MProf_Free(void *p, uintptr size) b->recent_free_bytes += size; } runtime_unlock(&proflock); - m = runtime_m(); - m->nomemprof--; } int64 runtime_blockprofilerate; // in CPU ticks @@ -347,7 +295,17 @@ void runtime_SetBlockProfileRate(intgo) __asm__ (GOSYM_PREFIX "runtime.SetBlockP void runtime_SetBlockProfileRate(intgo rate) { - runtime_atomicstore64((uint64*)&runtime_blockprofilerate, rate * runtime_tickspersecond() / (1000*1000*1000)); + int64 r; + + if(rate <= 0) + r = 0; // disable profiling + else { + // convert ns to cycles, use float64 to prevent overflow during multiplication + r = (float64)rate*runtime_tickspersecond()/(1000*1000*1000); + if(r == 0) + r = 1; + } + runtime_atomicstore64((uint64*)&runtime_blockprofilerate, r); } void @@ -510,10 +468,10 @@ func Stack(b Slice, all bool) (n int) { bool enablegc; sp = runtime_getcallersp(&b); - pc = runtime_getcallerpc(&b); + pc = (byte*)(uintptr)runtime_getcallerpc(&b); if(all) { - runtime_semacquire(&runtime_worldsema); + runtime_semacquire(&runtime_worldsema, false); runtime_m()->gcing = 1; runtime_stoptheworld(); enablegc = mstats.enablegc; @@ -530,7 +488,7 @@ func Stack(b Slice, all bool) (n int) { USED(sp); runtime_goroutineheader(g); runtime_traceback(); - runtime_goroutinetrailer(g); + runtime_printcreatedby(g); if(all) runtime_tracebackothers(g); n = b.__count - g->writenbuf; @@ -572,7 +530,7 @@ func GoroutineProfile(b Slice) (n int, ok bool) { ok = false; n = runtime_gcount(); if(n <= b.__count) { - runtime_semacquire(&runtime_worldsema); + runtime_semacquire(&runtime_worldsema, false); runtime_m()->gcing = 1; runtime_stoptheworld(); @@ -598,5 +556,5 @@ func GoroutineProfile(b Slice) (n int, ok bool) { void runtime_mprofinit(void) { - addrhash = allocate((1<<AddrHashBits)*sizeof *addrhash); + addrhash = runtime_persistentalloc((1<<AddrHashBits)*sizeof *addrhash, 0, &mstats.buckhash_sys); } diff --git a/libgo/runtime/msize.c b/libgo/runtime/msize.c index 3b5591c..745a769 100644 --- a/libgo/runtime/msize.c +++ b/libgo/runtime/msize.c @@ -31,7 +31,6 @@ int32 runtime_class_to_size[NumSizeClasses]; int32 runtime_class_to_allocnpages[NumSizeClasses]; -int32 runtime_class_to_transfercount[NumSizeClasses]; // The SizeToClass lookup is implemented using two arrays, // one mapping sizes <= 1024 to their class and one mapping @@ -42,17 +41,17 @@ int32 runtime_class_to_transfercount[NumSizeClasses]; // size divided by 128 (rounded up). The arrays are filled in // by InitSizes. -static int32 size_to_class8[1024/8 + 1]; -static int32 size_to_class128[(MaxSmallSize-1024)/128 + 1]; +int8 runtime_size_to_class8[1024/8 + 1]; +int8 runtime_size_to_class128[(MaxSmallSize-1024)/128 + 1]; -int32 -runtime_SizeToClass(int32 size) +static int32 +SizeToClass(int32 size) { if(size > MaxSmallSize) runtime_throw("SizeToClass - invalid size"); if(size > 1024-8) - return size_to_class128[(size-1024+127) >> 7]; - return size_to_class8[(size+7)>>3]; + return runtime_size_to_class128[(size-1024+127) >> 7]; + return runtime_size_to_class8[(size+7)>>3]; } void @@ -111,16 +110,16 @@ runtime_InitSizes(void) nextsize = 0; for (sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) { for(; nextsize < 1024 && nextsize <= runtime_class_to_size[sizeclass]; nextsize+=8) - size_to_class8[nextsize/8] = sizeclass; + runtime_size_to_class8[nextsize/8] = sizeclass; if(nextsize >= 1024) for(; nextsize <= runtime_class_to_size[sizeclass]; nextsize += 128) - size_to_class128[(nextsize-1024)/128] = sizeclass; + runtime_size_to_class128[(nextsize-1024)/128] = sizeclass; } // Double-check SizeToClass. if(0) { for(n=0; n < MaxSmallSize; n++) { - sizeclass = runtime_SizeToClass(n); + sizeclass = SizeToClass(n); if(sizeclass < 1 || sizeclass >= NumSizeClasses || runtime_class_to_size[sizeclass] < n) { runtime_printf("size=%d sizeclass=%d runtime_class_to_size=%d\n", n, sizeclass, runtime_class_to_size[sizeclass]); runtime_printf("incorrect SizeToClass"); @@ -137,16 +136,6 @@ runtime_InitSizes(void) // Copy out for statistics table. for(i=0; i<nelem(runtime_class_to_size); i++) mstats.by_size[i].size = runtime_class_to_size[i]; - - // Initialize the runtime_class_to_transfercount table. - for(sizeclass = 1; sizeclass < NumSizeClasses; sizeclass++) { - n = 64*1024 / runtime_class_to_size[sizeclass]; - if(n < 2) - n = 2; - if(n > 32) - n = 32; - runtime_class_to_transfercount[sizeclass] = n; - } return; dump: @@ -157,12 +146,14 @@ dump: runtime_printf(" %d", runtime_class_to_size[sizeclass]); runtime_printf("\n\n"); runtime_printf("size_to_class8:"); - for(i=0; i<nelem(size_to_class8); i++) - runtime_printf(" %d=>%d(%d)\n", i*8, size_to_class8[i], runtime_class_to_size[size_to_class8[i]]); + for(i=0; i<nelem(runtime_size_to_class8); i++) + runtime_printf(" %d=>%d(%d)\n", i*8, runtime_size_to_class8[i], + runtime_class_to_size[runtime_size_to_class8[i]]); runtime_printf("\n"); runtime_printf("size_to_class128:"); - for(i=0; i<nelem(size_to_class128); i++) - runtime_printf(" %d=>%d(%d)\n", i*128, size_to_class128[i], runtime_class_to_size[size_to_class128[i]]); + for(i=0; i<nelem(runtime_size_to_class128); i++) + runtime_printf(" %d=>%d(%d)\n", i*128, runtime_size_to_class128[i], + runtime_class_to_size[runtime_size_to_class128[i]]); runtime_printf("\n"); } runtime_throw("InitSizes failed"); diff --git a/libgo/runtime/netpoll.goc b/libgo/runtime/netpoll.goc index a0bd735..0270573 100644 --- a/libgo/runtime/netpoll.goc +++ b/libgo/runtime/netpoll.goc @@ -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 linux +// +build darwin dragonfly freebsd linux netbsd openbsd windows package net @@ -19,7 +19,7 @@ package net // Integrated network poller (platform-independent part). // A particular implementation (epoll/kqueue) must define the following functions: // void runtime_netpollinit(void); // to initialize the poller -// int32 runtime_netpollopen(int32 fd, PollDesc *pd); // to arm edge-triggered notifications +// int32 runtime_netpollopen(uintptr fd, PollDesc *pd); // to arm edge-triggered notifications // and associate fd with pd. // An implementation must call the following function to denote that the pd is ready. // void runtime_netpollready(G **gpp, PollDesc *pd, int32 mode); @@ -30,7 +30,7 @@ struct PollDesc { PollDesc* link; // in pollcache, protected by pollcache.Lock Lock; // protectes the following fields - int32 fd; + uintptr fd; bool closing; uintptr seq; // protects from stale timers and ready notifications G* rg; // G waiting for read or READY (binary semaphore) @@ -52,8 +52,8 @@ static struct // seq is incremented when deadlines are changed or descriptor is reused. } pollcache; -static void netpollblock(PollDesc*, int32); -static G* netpollunblock(PollDesc*, int32); +static bool netpollblock(PollDesc*, int32); +static G* netpollunblock(PollDesc*, int32, bool); static void deadline(int64, Eface); static void readDeadline(int64, Eface); static void writeDeadline(int64, Eface); @@ -68,7 +68,7 @@ func runtime_pollServerInit() { runtime_netpollinit(); } -func runtime_pollOpen(fd int) (pd *PollDesc, errno int) { +func runtime_pollOpen(fd uintptr) (pd *PollDesc, errno int) { pd = allocPollDesc(); runtime_lock(pd); if(pd->wg != nil && pd->wg != READY) @@ -117,18 +117,35 @@ ret: func runtime_pollWait(pd *PollDesc, mode int) (err int) { runtime_lock(pd); err = checkerr(pd, mode); - if(err) - goto ret; - netpollblock(pd, mode); - err = checkerr(pd, mode); -ret: + if(err == 0) { + while(!netpollblock(pd, mode)) { + err = checkerr(pd, mode); + if(err != 0) + break; + // Can happen if timeout has fired and unblocked us, + // but before we had a chance to run, timeout has been reset. + // Pretend it has not happened and retry. + } + } + runtime_unlock(pd); +} + +func runtime_pollWaitCanceled(pd *PollDesc, mode int) { + runtime_lock(pd); + // wait for ioready, ignore closing or timeouts. + while(!netpollblock(pd, mode)) + ; runtime_unlock(pd); } func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) { + G *rg, *wg; + runtime_lock(pd); - if(pd->closing) - goto ret; + if(pd->closing) { + runtime_unlock(pd); + return; + } pd->seq++; // invalidate current timers // Reset current timers. if(pd->rt.fv) { @@ -140,9 +157,8 @@ func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) { pd->wt.fv = nil; } // Setup new timers. - if(d != 0 && d <= runtime_nanotime()) { + if(d != 0 && d <= runtime_nanotime()) d = -1; - } if(mode == 'r' || mode == 'r'+'w') pd->rd = d; if(mode == 'w' || mode == 'r'+'w') @@ -172,8 +188,18 @@ func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) { runtime_addtimer(&pd->wt); } } -ret: + // If we set the new deadline in the past, unblock currently pending IO if any. + rg = nil; + wg = nil; + if(pd->rd < 0) + rg = netpollunblock(pd, 'r', false); + if(pd->wd < 0) + wg = netpollunblock(pd, 'w', false); runtime_unlock(pd); + if(rg) + runtime_ready(rg); + if(wg) + runtime_ready(wg); } func runtime_pollUnblock(pd *PollDesc) { @@ -184,8 +210,8 @@ func runtime_pollUnblock(pd *PollDesc) { runtime_throw("runtime_pollUnblock: already closing"); pd->closing = true; pd->seq++; - rg = netpollunblock(pd, 'r'); - wg = netpollunblock(pd, 'w'); + rg = netpollunblock(pd, 'r', false); + wg = netpollunblock(pd, 'w', false); if(pd->rt.fv) { runtime_deltimer(&pd->rt); pd->rt.fv = nil; @@ -201,6 +227,12 @@ func runtime_pollUnblock(pd *PollDesc) { runtime_ready(wg); } +uintptr +runtime_netpollfd(PollDesc *pd) +{ + return pd->fd; +} + // make pd ready, newly runnable goroutines (if any) are enqueued info gpp list void runtime_netpollready(G **gpp, PollDesc *pd, int32 mode) @@ -210,9 +242,9 @@ runtime_netpollready(G **gpp, PollDesc *pd, int32 mode) rg = wg = nil; runtime_lock(pd); if(mode == 'r' || mode == 'r'+'w') - rg = netpollunblock(pd, 'r'); + rg = netpollunblock(pd, 'r', true); if(mode == 'w' || mode == 'r'+'w') - wg = netpollunblock(pd, 'w'); + wg = netpollunblock(pd, 'w', true); runtime_unlock(pd); if(rg) { rg->schedlink = *gpp; @@ -234,7 +266,8 @@ checkerr(PollDesc *pd, int32 mode) return 0; } -static void +// returns true if IO is ready, or false if timedout or closed +static bool netpollblock(PollDesc *pd, int32 mode) { G **gpp; @@ -244,17 +277,20 @@ netpollblock(PollDesc *pd, int32 mode) gpp = &pd->wg; if(*gpp == READY) { *gpp = nil; - return; + return true; } if(*gpp != nil) - runtime_throw("epoll: double wait"); + runtime_throw("netpollblock: double wait"); *gpp = runtime_g(); runtime_park(runtime_unlock, &pd->Lock, "IO wait"); runtime_lock(pd); + if(runtime_g()->param) + return true; + return false; } static G* -netpollunblock(PollDesc *pd, int32 mode) +netpollunblock(PollDesc *pd, int32 mode, bool ioready) { G **gpp, *old; @@ -264,10 +300,15 @@ netpollunblock(PollDesc *pd, int32 mode) if(*gpp == READY) return nil; if(*gpp == nil) { - *gpp = READY; + // Only set READY for ioready. runtime_pollWait + // will check for timeout/cancel before waiting. + if(ioready) + *gpp = READY; return nil; } old = *gpp; + // pass unblock reason onto blocked g + old->param = (void*)(uintptr)ioready; *gpp = nil; return old; } @@ -296,14 +337,14 @@ deadlineimpl(int64 now, Eface arg, bool read, bool write) runtime_throw("deadlineimpl: inconsistent read deadline"); pd->rd = -1; pd->rt.fv = nil; - rg = netpollunblock(pd, 'r'); + rg = netpollunblock(pd, 'r', false); } if(write) { if(pd->wd <= 0 || (pd->wt.fv == nil && !read)) runtime_throw("deadlineimpl: inconsistent write deadline"); pd->wd = -1; pd->wt.fv = nil; - wg = netpollunblock(pd, 'w'); + wg = netpollunblock(pd, 'w', false); } runtime_unlock(pd); if(rg) @@ -343,7 +384,7 @@ allocPollDesc(void) n = 1; // Must be in non-GC memory because can be referenced // only from epoll/kqueue internals. - pd = runtime_SysAlloc(n*sizeof(*pd)); + pd = runtime_persistentalloc(n*sizeof(*pd), 0, &mstats.other_sys); for(i = 0; i < n; i++) { pd[i].link = pollcache.first; pollcache.first = &pd[i]; diff --git a/libgo/runtime/netpoll_epoll.c b/libgo/runtime/netpoll_epoll.c index 98c5cbe..b98aa81 100644 --- a/libgo/runtime/netpoll_epoll.c +++ b/libgo/runtime/netpoll_epoll.c @@ -94,24 +94,24 @@ runtime_netpollinit(void) } int32 -runtime_netpollopen(int32 fd, PollDesc *pd) +runtime_netpollopen(uintptr fd, PollDesc *pd) { EpollEvent ev; int32 res; ev.events = EPOLLIN|EPOLLOUT|EPOLLRDHUP|EPOLLET; ev.data.ptr = (void*)pd; - res = runtime_epollctl(epfd, EPOLL_CTL_ADD, fd, &ev); + res = runtime_epollctl(epfd, EPOLL_CTL_ADD, (int32)fd, &ev); return -res; } int32 -runtime_netpollclose(int32 fd) +runtime_netpollclose(uintptr fd) { EpollEvent ev; int32 res; - res = runtime_epollctl(epfd, EPOLL_CTL_DEL, fd, &ev); + res = runtime_epollctl(epfd, EPOLL_CTL_DEL, (int32)fd, &ev); return -res; } diff --git a/libgo/runtime/netpoll_kqueue.c b/libgo/runtime/netpoll_kqueue.c index 9b79b20..7890161 100644 --- a/libgo/runtime/netpoll_kqueue.c +++ b/libgo/runtime/netpoll_kqueue.c @@ -2,10 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// +build darwin +// +build darwin dragonfly freebsd netbsd openbsd #include "runtime.h" #include "defs_GOOS_GOARCH.h" +#include "os_GOOS.h" // Integrated network poller (kqueue-based implementation). @@ -27,7 +28,7 @@ runtime_netpollinit(void) } int32 -runtime_netpollopen(int32 fd, PollDesc *pd) +runtime_netpollopen(uintptr fd, PollDesc *pd) { Kevent ev[2]; int32 n; @@ -35,30 +36,22 @@ runtime_netpollopen(int32 fd, PollDesc *pd) // Arm both EVFILT_READ and EVFILT_WRITE in edge-triggered mode (EV_CLEAR) // for the whole fd lifetime. The notifications are automatically unregistered // when fd is closed. - ev[0].ident = fd; + ev[0].ident = (uint32)fd; ev[0].filter = EVFILT_READ; - ev[0].flags = EV_ADD|EV_RECEIPT|EV_CLEAR; + ev[0].flags = EV_ADD|EV_CLEAR; ev[0].fflags = 0; ev[0].data = 0; - ev[0].udata = (byte*)pd; + ev[0].udata = (kevent_udata)pd; ev[1] = ev[0]; ev[1].filter = EVFILT_WRITE; - n = runtime_kevent(kq, ev, 2, ev, 2, nil); + n = runtime_kevent(kq, ev, 2, nil, 0, nil); if(n < 0) return -n; - if(n != 2 || - (ev[0].flags&EV_ERROR) == 0 || ev[0].ident != fd || ev[0].filter != EVFILT_READ || - (ev[1].flags&EV_ERROR) == 0 || ev[1].ident != fd || ev[1].filter != EVFILT_WRITE) - return EFAULT; // just to mark out from other errors - if(ev[0].data != 0) - return ev[0].data; - if(ev[1].data != 0) - return ev[1].data; return 0; } int32 -runtime_netpollclose(int32 fd) +runtime_netpollclose(uintptr fd) { // Don't need to unregister because calling close() // on fd will remove any kevents that reference the descriptor. @@ -74,7 +67,7 @@ runtime_netpoll(bool block) static int32 lasterr; Kevent events[64], *ev; Timespec ts, *tp; - int32 n, i; + int32 n, i, mode; G *gp; if(kq == -1) @@ -97,10 +90,13 @@ retry: } for(i = 0; i < n; i++) { ev = &events[i]; + mode = 0; if(ev->filter == EVFILT_READ) - runtime_netpollready(&gp, (PollDesc*)ev->udata, 'r'); + mode += 'r'; if(ev->filter == EVFILT_WRITE) - runtime_netpollready(&gp, (PollDesc*)ev->udata, 'w'); + mode += 'w'; + if(mode) + runtime_netpollready(&gp, (PollDesc*)ev->udata, mode); } if(block && gp == nil) goto retry; diff --git a/libgo/runtime/netpoll_stub.c b/libgo/runtime/netpoll_stub.c index e28e38e..84eef75 100644 --- a/libgo/runtime/netpoll_stub.c +++ b/libgo/runtime/netpoll_stub.c @@ -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 freebsd netbsd openbsd plan9 windows +// +build plan9 #include "runtime.h" diff --git a/libgo/runtime/panic.c b/libgo/runtime/panic.c index 7d79256..7a8d95b 100644 --- a/libgo/runtime/panic.c +++ b/libgo/runtime/panic.c @@ -38,7 +38,7 @@ runtime_startpanic(void) M *m; m = runtime_m(); - if(runtime_mheap == 0 || runtime_mheap->cachealloc.size == 0) { // very early + if(runtime_mheap.cachealloc.size == 0) { // very early runtime_printf("runtime: panic before malloc heap initialized\n"); m->mallocing = 1; // tell rest of panic not to try to malloc } else if(m->mcache == nil) // can happen if called from signal handler or throw @@ -48,8 +48,13 @@ runtime_startpanic(void) runtime_exit(3); } m->dying = 1; + if(runtime_g() != nil) + runtime_g()->writebuf = nil; runtime_xadd(&runtime_panicking, 1); runtime_lock(&paniclk); + if(runtime_debug.schedtrace > 0 || runtime_debug.scheddetail > 0) + runtime_schedtrace(true); + runtime_freezetheworld(); } void @@ -58,18 +63,22 @@ runtime_dopanic(int32 unused __attribute__ ((unused))) G *g; static bool didothers; bool crash; + int32 t; g = runtime_g(); if(g->sig != 0) runtime_printf("[signal %x code=%p addr=%p]\n", g->sig, (void*)g->sigcode0, (void*)g->sigcode1); - if(runtime_gotraceback(&crash)){ + if((t = runtime_gotraceback(&crash)) > 0){ if(g != runtime_m()->g0) { runtime_printf("\n"); runtime_goroutineheader(g); runtime_traceback(); - runtime_goroutinetrailer(g); + runtime_printcreatedby(g); + } else if(t >= 2 || runtime_m()->throwing > 0) { + runtime_printf("\nruntime stack:\n"); + runtime_traceback(); } if(!didothers) { didothers = true; @@ -113,11 +122,15 @@ runtime_panicstring(const char *s) { Eface err; + if(runtime_m()->mallocing) { + runtime_printf("panic: %s\n", s); + runtime_throw("panic during malloc"); + } if(runtime_m()->gcing) { runtime_printf("panic: %s\n", s); runtime_throw("panic during gc"); } - runtime_newErrorString(runtime_gostringnocopy((const byte*)s), &err); + runtime_newErrorCString(s, &err); runtime_panic(err); } diff --git a/libgo/runtime/parfor.c b/libgo/runtime/parfor.c index c0e40f5..9489d8d 100644 --- a/libgo/runtime/parfor.c +++ b/libgo/runtime/parfor.c @@ -151,9 +151,9 @@ runtime_parfordo(ParFor *desc) if(victim >= tid) victim++; victimpos = &desc->thr[victim].pos; - pos = runtime_atomicload64(victimpos); for(;;) { // See if it has any work. + pos = runtime_atomicload64(victimpos); begin = (uint32)pos; end = (uint32)(pos>>32); if(begin+1 >= end) { @@ -166,7 +166,7 @@ runtime_parfordo(ParFor *desc) } begin2 = begin + (end-begin)/2; newpos = (uint64)begin | (uint64)begin2<<32; - if(runtime_cas64(victimpos, &pos, newpos)) { + if(runtime_cas64(victimpos, pos, newpos)) { begin = begin2; break; } diff --git a/libgo/runtime/print.c b/libgo/runtime/print.c index f5c6e82..766ddbd 100644 --- a/libgo/runtime/print.c +++ b/libgo/runtime/print.c @@ -5,6 +5,7 @@ #include <stdarg.h> #include "runtime.h" #include "array.h" +#include "go-type.h" //static Lock debuglock; @@ -13,7 +14,7 @@ static void go_vprintf(const char*, va_list); // write to goroutine-local buffer if diverting output, // or else standard error. static void -gwrite(const void *v, int32 n) +gwrite(const void *v, intgo n) { G* g = runtime_g(); @@ -301,8 +302,6 @@ runtime_printpointer(void *p) void runtime_printstring(String v) { - // extern uint32 runtime_maxstring; - // if(v.len > runtime_maxstring) { // gwrite("[string too long]", 17); // return; diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c index 0e77a3e..ab7cde4 100644 --- a/libgo/runtime/proc.c +++ b/libgo/runtime/proc.c @@ -231,8 +231,8 @@ kickoff(void) } // Switch context to a different goroutine. This is like longjmp. -static void runtime_gogo(G*) __attribute__ ((noinline)); -static void +void runtime_gogo(G*) __attribute__ ((noinline)); +void runtime_gogo(G* newg) { #ifdef USING_SPLIT_STACK @@ -249,8 +249,8 @@ runtime_gogo(G* newg) // setjmp. Because getcontext always returns 0, unlike setjmp, we use // g->fromgogo as a code. It will be true if we got here via // setcontext. g == nil the first time this is called in a new m. -static void runtime_mcall(void (*)(G*)) __attribute__ ((noinline)); -static void +void runtime_mcall(void (*)(G*)) __attribute__ ((noinline)); +void runtime_mcall(void (*pfn)(G*)) { M *mp; @@ -365,8 +365,9 @@ struct Sched { uint64 goidgen; M* midle; // idle m's waiting for work int32 nmidle; // number of idle m's waiting for work - int32 mlocked; // number of locked m's waiting for work + int32 nmidlelocked; // number of locked m's waiting for work int32 mcount; // number of m's that have been created + int32 maxmcount; // maximum number of m's allowed (or die) P* pidle; // idle P's uint32 npidle; @@ -381,6 +382,7 @@ struct Sched { Lock gflock; G* gfree; + uint32 gcwaiting; // gc is waiting to run int32 stopwait; Note stopnote; uint32 sysmonwait; @@ -396,10 +398,8 @@ enum { MaxGomaxprocs = 1<<8 }; Sched runtime_sched; int32 runtime_gomaxprocs; -bool runtime_singleproc; -bool runtime_iscgo = true; uint32 runtime_needextram = 1; -uint32 runtime_gcwaiting; +bool runtime_iscgo = true; M runtime_m0; G runtime_g0; // idle goroutine for m0 G* runtime_allg; @@ -409,6 +409,7 @@ P** runtime_allp; M* runtime_extram; int8* runtime_goos; int32 runtime_ncpu; +bool runtime_precisestack; static int32 newprocs; void* runtime_mstart(void*); @@ -431,21 +432,22 @@ static void wakep(void); static void stoplockedm(void); static void startlockedm(G*); static void sysmon(void); -static uint32 retake(uint32*); -static void inclocked(int32); +static uint32 retake(int64); +static void incidlelocked(int32); static void checkdead(void); static void exitsyscall0(G*); static void park0(G*); -static void gosched0(G*); static void goexit0(G*); static void gfput(P*, G*); static G* gfget(P*); static void gfpurge(P*); static void globrunqput(G*); -static G* globrunqget(P*); +static G* globrunqget(P*, int32); static P* pidleget(void); static void pidleput(P*); static void injectglist(G*); +static bool preemptall(void); +static bool exitsyscallfast(void); // The bootstrap sequence is: // @@ -460,6 +462,7 @@ runtime_schedinit(void) { int32 n, procs; const byte *p; + Eface i; m = &runtime_m0; g = &runtime_g0; @@ -470,18 +473,22 @@ runtime_schedinit(void) initcontext(); inittlssize(); - m->nomemprof++; + runtime_sched.maxmcount = 10000; + runtime_precisestack = 0; + runtime_mprofinit(); runtime_mallocinit(); mcommoninit(m); + + // Initialize the itable value for newErrorCString, + // so that the next time it gets called, possibly + // in a fault during a garbage collection, it will not + // need to allocated memory. + runtime_newErrorCString(0, &i); runtime_goargs(); runtime_goenvs(); - - // For debugging: - // Allocate internal symbol table representation now, - // so that we don't need to call malloc when we crash. - // runtime_findfunc(0); + runtime_parsedebugvars(); runtime_sched.lastpoll = runtime_nanotime(); procs = 1; @@ -496,16 +503,26 @@ runtime_schedinit(void) // Can not enable GC until all roots are registered. // mstats.enablegc = 1; - m->nomemprof--; + + // if(raceenabled) + // g->racectx = runtime_raceinit(); } extern void main_init(void) __asm__ (GOSYM_PREFIX "__go_init_main"); extern void main_main(void) __asm__ (GOSYM_PREFIX "main.main"); +static void +initDone(void *arg __attribute__ ((unused))) { + runtime_unlockOSThread(); +}; + // The main goroutine. void runtime_main(void* dummy __attribute__((unused))) { + Defer d; + _Bool frame; + newm(sysmon, nil); // Lock the main goroutine onto this, the main OS thread, @@ -515,10 +532,24 @@ runtime_main(void* dummy __attribute__((unused))) // by calling runtime.LockOSThread during initialization // to preserve the lock. runtime_lockOSThread(); + + // Defer unlock so that runtime.Goexit during init does the unlock too. + d.__pfn = initDone; + d.__next = g->defer; + d.__arg = (void*)-1; + d.__panic = g->panic; + d.__retaddr = nil; + d.__frame = &frame; + g->defer = &d; + if(m != &runtime_m0) runtime_throw("runtime_main not on m0"); __go_go(runtime_MHeap_Scavenger, nil); main_init(); + + if(g->defer != &d || d.__pfn != initDone) + runtime_throw("runtime: bad defer entry after init"); + g->defer = d.__next; runtime_unlockOSThread(); // For gccgo we have to wait until after main is initialized @@ -574,7 +605,7 @@ runtime_goroutineheader(G *gp) } void -runtime_goroutinetrailer(G *g) +runtime_printcreatedby(G *g) { if(g != nil && g->gopc != 0 && g->goid != 1) { String fn; @@ -604,8 +635,28 @@ runtime_tracebackothers(G * volatile me) tb.gp = me; traceback = runtime_gotraceback(nil); + + // Show the current goroutine first, if we haven't already. + if((gp = m->curg) != nil && gp != me) { + runtime_printf("\n"); + runtime_goroutineheader(gp); + gp->traceback = &tb; + +#ifdef USING_SPLIT_STACK + __splitstack_getcontext(&me->stack_context[0]); +#endif + getcontext(&me->context); + + if(gp->traceback != nil) { + runtime_gogo(gp); + } + + runtime_printtrace(tb.locbuf, tb.c, false); + runtime_printcreatedby(gp); + } + for(gp = runtime_allg; gp != nil; gp = gp->alllink) { - if(gp == me || gp->status == Gdead) + if(gp == me || gp == m->curg || gp->status == Gdead) continue; if(gp->issystem && traceback < 2) continue; @@ -620,25 +671,38 @@ runtime_tracebackothers(G * volatile me) // This means that if g is running or in a syscall, we // can't reliably print a stack trace. FIXME. - if(gp->status == Gsyscall || gp->status == Grunning) { - runtime_printf("no stack trace available\n"); - runtime_goroutinetrailer(gp); - continue; - } - gp->traceback = &tb; + if(gp->status == Grunning) { + runtime_printf("\tgoroutine running on other thread; stack unavailable\n"); + runtime_printcreatedby(gp); + } else if(gp->status == Gsyscall) { + runtime_printf("\tgoroutine in C code; stack unavailable\n"); + runtime_printcreatedby(gp); + } else { + gp->traceback = &tb; #ifdef USING_SPLIT_STACK - __splitstack_getcontext(&me->stack_context[0]); + __splitstack_getcontext(&me->stack_context[0]); #endif - getcontext(&me->context); + getcontext(&me->context); - if(gp->traceback != nil) { - runtime_gogo(gp); + if(gp->traceback != nil) { + runtime_gogo(gp); + } + + runtime_printtrace(tb.locbuf, tb.c, false); + runtime_printcreatedby(gp); } + } +} - runtime_printtrace(tb.locbuf, tb.c, false); - runtime_goroutinetrailer(gp); +static void +checkmcount(void) +{ + // sched lock is held + if(runtime_sched.mcount > runtime_sched.maxmcount) { + runtime_printf("runtime: program exceeds %d-thread limit\n", runtime_sched.maxmcount); + runtime_throw("thread exhaustion"); } } @@ -669,7 +733,7 @@ mcommoninit(M *mp) runtime_lock(&runtime_sched); mp->id = runtime_sched.mcount++; - + checkmcount(); runtime_mpreinit(mp); // Add to runtime_allm so garbage collector doesn't free m @@ -686,6 +750,7 @@ void runtime_ready(G *gp) { // Mark runnable. + m->locks++; // disable preemption because it can be holding p in a local var if(gp->status != Gwaiting) { runtime_printf("goroutine %D has status %d\n", gp->goid, gp->status); runtime_throw("bad g->status in ready"); @@ -694,6 +759,7 @@ runtime_ready(G *gp) runqput(m->p, gp); if(runtime_atomicload(&runtime_sched.npidle) != 0 && runtime_atomicload(&runtime_sched.nmspinning) == 0) // TODO: fast atomic wakep(); + m->locks--; } int32 @@ -753,6 +819,34 @@ runtime_helpgc(int32 nproc) runtime_unlock(&runtime_sched); } +// Similar to stoptheworld but best-effort and can be called several times. +// There is no reverse operation, used during crashing. +// This function must not lock any mutexes. +void +runtime_freezetheworld(void) +{ + int32 i; + + if(runtime_gomaxprocs == 1) + return; + // stopwait and preemption requests can be lost + // due to races with concurrently executing threads, + // so try several times + for(i = 0; i < 5; i++) { + // this should tell the scheduler to not start any new goroutines + runtime_sched.stopwait = 0x7fffffff; + runtime_atomicstore((uint32*)&runtime_sched.gcwaiting, 1); + // this should stop running goroutines + if(!preemptall()) + break; // no running goroutines + runtime_usleep(1000); + } + // to be sure + runtime_usleep(1000); + preemptall(); + runtime_usleep(1000); +} + void runtime_stoptheworld(void) { @@ -763,7 +857,8 @@ runtime_stoptheworld(void) runtime_lock(&runtime_sched); runtime_sched.stopwait = runtime_gomaxprocs; - runtime_atomicstore((uint32*)&runtime_gcwaiting, 1); + runtime_atomicstore((uint32*)&runtime_sched.gcwaiting, 1); + preemptall(); // stop current P m->p->status = Pgcstop; runtime_sched.stopwait--; @@ -782,7 +877,7 @@ runtime_stoptheworld(void) wait = runtime_sched.stopwait > 0; runtime_unlock(&runtime_sched); - // wait for remaining P's to stop voluntary + // wait for remaining P's to stop voluntarily if(wait) { runtime_notesleep(&runtime_sched.stopnote); runtime_noteclear(&runtime_sched.stopnote); @@ -810,6 +905,7 @@ runtime_starttheworld(void) G *gp; bool add; + m->locks++; // disable preemption because it can be holding p in a local var gp = runtime_netpoll(false); // non-blocking injectglist(gp); add = needaddgcproc(); @@ -819,7 +915,7 @@ runtime_starttheworld(void) newprocs = 0; } else procresize(runtime_gomaxprocs); - runtime_gcwaiting = 0; + runtime_sched.gcwaiting = 0; p1 = nil; while((p = pidleget()) != nil) { @@ -829,16 +925,9 @@ runtime_starttheworld(void) pidleput(p); break; } - mp = mget(); - if(mp == nil) { - p->link = p1; - p1 = p; - continue; - } - if(mp->nextp) - runtime_throw("starttheworld: inconsistent mp->nextp"); - mp->nextp = p; - runtime_notewakeup(&mp->park); + p->m = mget(); + p->link = p1; + p1 = p; } if(runtime_sched.sysmonwait) { runtime_sched.sysmonwait = false; @@ -849,8 +938,18 @@ runtime_starttheworld(void) while(p1) { p = p1; p1 = p1->link; - add = false; - newm(nil, p); + if(p->m) { + mp = p->m; + p->m = nil; + if(mp->nextp) + runtime_throw("starttheworld: inconsistent mp->nextp"); + mp->nextp = p; + runtime_notewakeup(&mp->park); + } else { + // Start M to run P. Do not start another M below. + newm(nil, p); + add = false; + } } if(add) { @@ -863,6 +962,7 @@ runtime_starttheworld(void) // the maximum number of procs. newm(mhelpgc, nil); } + m->locks--; } // Called to start an M. @@ -909,11 +1009,8 @@ runtime_mstart(void* mp) // Install signal handlers; after minit so that minit can // prepare the thread to be able to handle the signals. - if(m == &runtime_m0) { + if(m == &runtime_m0) runtime_initsig(); - if(runtime_iscgo) - runtime_newextram(); - } if(m->mstartfn) m->mstartfn(); @@ -1015,6 +1112,14 @@ runtime_needm(void) { M *mp; + if(runtime_needextram) { + // Can happen if C/C++ code calls Go from a global ctor. + // Can not throw, because scheduler is not initialized yet. + runtime_write(2, "fatal error: cgo callback before cgo call\n", + sizeof("fatal error: cgo callback before cgo call\n")-1); + runtime_exit(1); + } + // Lock extra list, take head, unlock popped list. // nilokay=false is safe here because of the invariant above, // that the extra list always contains or will soon contain @@ -1090,6 +1195,7 @@ runtime_newextram(void) mp->locked = LockInternal; mp->lockedg = gp; gp->lockedm = mp; + gp->goid = runtime_xadd64(&runtime_sched.goidgen, 1); // put on allg for garbage collector runtime_lock(&runtime_sched); if(runtime_lastg == nil) @@ -1325,7 +1431,7 @@ handoffp(P *p) return; } runtime_lock(&runtime_sched); - if(runtime_gcwaiting) { + if(runtime_sched.gcwaiting) { p->status = Pgcstop; if(--runtime_sched.stopwait == 0) runtime_notewakeup(&runtime_sched.stopnote); @@ -1373,7 +1479,7 @@ stoplockedm(void) p = releasep(); handoffp(p); } - inclocked(1); + incidlelocked(1); // Wait until another thread schedules lockedg again. runtime_notesleep(&m->park); runtime_noteclear(&m->park); @@ -1396,7 +1502,7 @@ startlockedm(G *gp) if(mp->nextp) runtime_throw("startlockedm: m has p"); // directly handoff current P to the locked m - inclocked(-1); + incidlelocked(-1); p = releasep(); mp->nextp = p; runtime_notewakeup(&mp->park); @@ -1410,7 +1516,7 @@ gcstopm(void) { P *p; - if(!runtime_gcwaiting) + if(!runtime_sched.gcwaiting) runtime_throw("gcstopm: not waiting for gc"); if(m->spinning) { m->spinning = false; @@ -1437,7 +1543,7 @@ execute(G *gp) runtime_throw("execute: bad g status"); } gp->status = Grunning; - m->p->tick++; + m->p->schedtick++; m->curg = gp; gp->m = m; @@ -1459,7 +1565,7 @@ findrunnable(void) int32 i; top: - if(runtime_gcwaiting) { + if(runtime_sched.gcwaiting) { gcstopm(); goto top; } @@ -1470,7 +1576,7 @@ top: // global runq if(runtime_sched.runqsize) { runtime_lock(&runtime_sched); - gp = globrunqget(m->p); + gp = globrunqget(m->p, 0); runtime_unlock(&runtime_sched); if(gp) return gp; @@ -1493,7 +1599,7 @@ top: } // random steal from other P's for(i = 0; i < 2*runtime_gomaxprocs; i++) { - if(runtime_gcwaiting) + if(runtime_sched.gcwaiting) goto top; p = runtime_allp[runtime_fastrand1()%runtime_gomaxprocs]; if(p == m->p) @@ -1506,12 +1612,12 @@ top: stop: // return P and block runtime_lock(&runtime_sched); - if(runtime_gcwaiting) { + if(runtime_sched.gcwaiting) { runtime_unlock(&runtime_sched); goto top; } if(runtime_sched.runqsize) { - gp = globrunqget(m->p); + gp = globrunqget(m->p, 0); runtime_unlock(&runtime_sched); return gp; } @@ -1561,6 +1667,25 @@ stop: goto top; } +static void +resetspinning(void) +{ + int32 nmspinning; + + if(m->spinning) { + m->spinning = false; + nmspinning = runtime_xadd(&runtime_sched.nmspinning, -1); + if(nmspinning < 0) + runtime_throw("findrunnable: negative nmspinning"); + } else + nmspinning = runtime_atomicload(&runtime_sched.nmspinning); + + // M wakeup policy is deliberately somewhat conservative (see nmspinning handling), + // so see if we need to wakeup another P here. + if (nmspinning == 0 && runtime_atomicload(&runtime_sched.npidle) > 0) + wakep(); +} + // Injects the list of runnable G's into the scheduler. // Can run concurrently with GC. static void @@ -1590,33 +1715,44 @@ static void schedule(void) { G *gp; + uint32 tick; if(m->locks) runtime_throw("schedule: holding locks"); top: - if(runtime_gcwaiting) { + if(runtime_sched.gcwaiting) { gcstopm(); goto top; } - gp = runqget(m->p); - if(gp == nil) - gp = findrunnable(); - - if(m->spinning) { - m->spinning = false; - runtime_xadd(&runtime_sched.nmspinning, -1); + gp = nil; + // Check the global runnable queue once in a while to ensure fairness. + // Otherwise two goroutines can completely occupy the local runqueue + // by constantly respawning each other. + tick = m->p->schedtick; + // This is a fancy way to say tick%61==0, + // it uses 2 MUL instructions instead of a single DIV and so is faster on modern processors. + if(tick - (((uint64)tick*0x4325c53fu)>>36)*61 == 0 && runtime_sched.runqsize > 0) { + runtime_lock(&runtime_sched); + gp = globrunqget(m->p, 1); + runtime_unlock(&runtime_sched); + if(gp) + resetspinning(); + } + if(gp == nil) { + gp = runqget(m->p); + if(gp && m->spinning) + runtime_throw("schedule: spinning with local work"); + } + if(gp == nil) { + gp = findrunnable(); // blocks until work is available + resetspinning(); } - - // M wakeup policy is deliberately somewhat conservative (see nmspinning handling), - // so see if we need to wakeup another M here. - if (m->p->runqhead != m->p->runqtail && - runtime_atomicload(&runtime_sched.nmspinning) == 0 && - runtime_atomicload(&runtime_sched.npidle) > 0) // TODO: fast atomic - wakep(); if(gp->lockedm) { + // Hands off own p to the locked m, + // then blocks waiting for a new p. startlockedm(gp); goto top; } @@ -1658,12 +1794,12 @@ park0(G *gp) void runtime_gosched(void) { - runtime_mcall(gosched0); + runtime_mcall(runtime_gosched0); } // runtime_gosched continuation on g0. -static void -gosched0(G *gp) +void +runtime_gosched0(G *gp) { gp->status = Grunnable; gp->m = nil; @@ -1679,6 +1815,9 @@ gosched0(G *gp) } // Finishes execution of the current goroutine. +// Need to mark it as nosplit, because it runs with sp > stackbase (as runtime_lessstack). +// Since it does not return it does not matter. But if it is preempted +// at the split stack check, GC will complain about inconsistent sp. void runtime_goexit(void) { @@ -1698,7 +1837,7 @@ goexit0(G *gp) m->curg = nil; m->lockedg = nil; if(m->locked & ~LockExternal) { - runtime_printf("invalid m->locked = %d", m->locked); + runtime_printf("invalid m->locked = %d\n", m->locked); runtime_throw("internal lockOSThread error"); } m->locked = 0; @@ -1720,10 +1859,11 @@ void runtime_entersyscall(void) __attribute__ ((no_split_stack)); void runtime_entersyscall() { - if(m->profilehz > 0) - runtime_setprof(false); + // Disable preemption because during this function g is in Gsyscall status, + // but can have inconsistent g->sched, do not let GC observe it. + m->locks++; - // Leave SP around for gc and traceback. + // Leave SP around for GC and traceback. #ifdef USING_SPLIT_STACK g->gcstack = __splitstack_find(nil, nil, &g->gcstack_size, &g->gcnext_segment, &g->gcnext_sp, @@ -1752,10 +1892,9 @@ runtime_entersyscall() } m->mcache = nil; - m->p->tick++; m->p->m = nil; runtime_atomicstore(&m->p->status, Psyscall); - if(runtime_gcwaiting) { + if(runtime_sched.gcwaiting) { runtime_lock(&runtime_sched); if (runtime_sched.stopwait > 0 && runtime_cas(&m->p->status, Psyscall, Pgcstop)) { if(--runtime_sched.stopwait == 0) @@ -1763,6 +1902,8 @@ runtime_entersyscall() } runtime_unlock(&runtime_sched); } + + m->locks--; } // The same as runtime_entersyscall(), but with a hint that the syscall is blocking. @@ -1771,10 +1912,9 @@ runtime_entersyscallblock(void) { P *p; - if(m->profilehz > 0) - runtime_setprof(false); + m->locks++; // see comment in entersyscall - // Leave SP around for gc and traceback. + // Leave SP around for GC and traceback. #ifdef USING_SPLIT_STACK g->gcstack = __splitstack_find(nil, nil, &g->gcstack_size, &g->gcnext_segment, &g->gcnext_sp, @@ -1792,7 +1932,9 @@ runtime_entersyscallblock(void) p = releasep(); handoffp(p); if(g->isbackground) // do not consider blocked scavenger for deadlock detection - inclocked(1); + incidlelocked(1); + + m->locks--; } // The goroutine g exited its system call. @@ -1803,19 +1945,16 @@ void runtime_exitsyscall(void) { G *gp; - P *p; - // Check whether the profiler needs to be turned on. - if(m->profilehz > 0) - runtime_setprof(true); + m->locks++; // see comment in entersyscall gp = g; - // Try to re-acquire the last P. - if(m->p && m->p->status == Psyscall && runtime_cas(&m->p->status, Psyscall, Prunning)) { + if(gp->isbackground) // do not consider blocked scavenger for deadlock detection + incidlelocked(-1); + + if(exitsyscallfast()) { // There's a cpu for us, so we can run. - m->mcache = m->p->mcache; - m->p->m = m; - m->p->tick++; + m->p->syscalltick++; gp->status = Grunning; // Garbage collector isn't running (since we are), // so okay to clear gcstack and gcsp. @@ -1824,27 +1963,11 @@ runtime_exitsyscall(void) #endif gp->gcnext_sp = nil; runtime_memclr(&gp->gcregs, sizeof gp->gcregs); + m->locks--; return; } - if(gp->isbackground) // do not consider blocked scavenger for deadlock detection - inclocked(-1); - // Try to get any other idle P. - m->p = nil; - if(runtime_sched.pidle) { - runtime_lock(&runtime_sched); - p = pidleget(); - runtime_unlock(&runtime_sched); - if(p) { - acquirep(p); -#ifdef USING_SPLIT_STACK - gp->gcstack = nil; -#endif - gp->gcnext_sp = nil; - runtime_memclr(&gp->gcregs, sizeof gp->gcregs); - return; - } - } + m->locks--; // Call the scheduler. runtime_mcall(exitsyscall0); @@ -1860,6 +1983,43 @@ runtime_exitsyscall(void) #endif gp->gcnext_sp = nil; runtime_memclr(&gp->gcregs, sizeof gp->gcregs); + m->p->syscalltick++; +} + +static bool +exitsyscallfast(void) +{ + P *p; + + // Freezetheworld sets stopwait but does not retake P's. + if(runtime_sched.stopwait) { + m->p = nil; + return false; + } + + // Try to re-acquire the last P. + if(m->p && m->p->status == Psyscall && runtime_cas(&m->p->status, Psyscall, Prunning)) { + // There's a cpu for us, so we can run. + m->mcache = m->p->mcache; + m->p->m = m; + return true; + } + // Try to get any other idle P. + m->p = nil; + if(runtime_sched.pidle) { + runtime_lock(&runtime_sched); + p = pidleget(); + if(p && runtime_atomicload(&runtime_sched.sysmonwait)) { + runtime_atomicstore(&runtime_sched.sysmonwait, 0); + runtime_notewakeup(&runtime_sched.sysmonnote); + } + runtime_unlock(&runtime_sched); + if(p) { + acquirep(p); + return true; + } + } + return false; } // runtime_exitsyscall slow path on g0. @@ -1876,6 +2036,10 @@ exitsyscall0(G *gp) p = pidleget(); if(p == nil) globrunqput(gp); + else if(runtime_atomicload(&runtime_sched.sysmonwait)) { + runtime_atomicstore(&runtime_sched.sysmonwait, 0); + runtime_notewakeup(&runtime_sched.sysmonnote); + } runtime_unlock(&runtime_sched); if(p) { acquirep(p); @@ -1890,6 +2054,33 @@ exitsyscall0(G *gp) schedule(); // Never returns. } +// Called from syscall package before fork. +void syscall_runtime_BeforeFork(void) + __asm__(GOSYM_PREFIX "syscall.runtime_BeforeFork"); +void +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_resetcpuprofiler(0); +} + +// Called from syscall package after fork in parent. +void syscall_runtime_AfterFork(void) + __asm__(GOSYM_PREFIX "syscall.runtime_AfterFork"); +void +syscall_runtime_AfterFork(void) +{ + int32 hz; + + hz = runtime_sched.profilehz; + if(hz != 0) + runtime_resetcpuprofiler(hz); + m->locks--; +} + // Allocate a new g, with a stack big enough for stacksize bytes. G* runtime_malg(int32 stacksize, byte** ret_stack, size_t* ret_stacksize) @@ -1919,9 +2110,16 @@ runtime_malg(int32 stacksize, byte** ret_stack, size_t* ret_stacksize) /* For runtime package testing. */ + +// Create a new g running fn with siz bytes of arguments. +// Put it on the queue of g's waiting to run. +// The compiler turns a go statement into a call to this. +// Cannot split the stack because it assumes that the arguments +// are available sequentially after &fn; they would not be +// copied if a stack split occurred. It's OK for this to call +// functions that split the stack. void runtime_testing_entersyscall(void) __asm__ (GOSYM_PREFIX "runtime.entersyscall"); - void runtime_testing_entersyscall() { @@ -1944,6 +2142,7 @@ __go_go(void (*fn)(void*), void* arg) size_t spsize; G *newg; +//runtime_printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret); m->locks++; // disable preemption because it can be holding p in a local var if((newg = gfget(m->p)) != nil) { @@ -2099,7 +2298,7 @@ runtime_gomaxprocsfunc(int32 n) } runtime_unlock(&runtime_sched); - runtime_semacquire(&runtime_worldsema); + runtime_semacquire(&runtime_worldsema, false); m->gcing = 1; runtime_stoptheworld(); newprocs = n; @@ -2110,8 +2309,11 @@ runtime_gomaxprocsfunc(int32 n) return ret; } +// lockOSThread is called by runtime.LockOSThread and runtime.lockOSThread below +// after they modify m->locked. Do not allow preemption during this call, +// or else the m might be different in this function than in the caller. static void -LockOSThread(void) +lockOSThread(void) { m->lockedg = g; g->lockedm = m; @@ -2122,18 +2324,22 @@ void runtime_LockOSThread(void) { m->locked |= LockExternal; - LockOSThread(); + lockOSThread(); } void runtime_lockOSThread(void) { m->locked += LockInternal; - LockOSThread(); + lockOSThread(); } + +// unlockOSThread is called by runtime.UnlockOSThread and runtime.unlockOSThread below +// after they update m->locked. Do not allow preemption during this call, +// or else the m might be in different in this function than in the caller. static void -UnlockOSThread(void) +unlockOSThread(void) { if(m->locked != 0) return; @@ -2147,7 +2353,7 @@ void runtime_UnlockOSThread(void) { m->locked &= ~LockExternal; - UnlockOSThread(); + unlockOSThread(); } void @@ -2156,7 +2362,7 @@ runtime_unlockOSThread(void) if(m->locked < LockInternal) runtime_throw("runtime: internal error: misuse of lockOSThread/unlockOSThread"); m->locked -= LockInternal; - UnlockOSThread(); + unlockOSThread(); } bool @@ -2176,13 +2382,6 @@ runtime_golockedOSThread(void) return runtime_lockedOSThread(); } -// for testing of wire, unwire -uint32 -runtime_mid() -{ - return m->id; -} - intgo runtime_NumGoroutine (void) __asm__ (GOSYM_PREFIX "runtime.NumGoroutine"); @@ -2227,28 +2426,42 @@ static struct { Location locbuf[100]; } prof; +static void +System(void) +{ +} + // Called if we receive a SIGPROF signal. void runtime_sigprof() { int32 n, i; + bool traceback; - // Windows does profiling in a dedicated thread w/o m. - if(!Windows && (m == nil || m->mcache == nil)) - return; if(prof.fn == nil || prof.hz == 0) return; - + traceback = true; + // Windows does profiling in a dedicated thread w/o m. + if(!Windows && (m == nil || m->mcache == nil)) + traceback = false; + runtime_lock(&prof); if(prof.fn == nil) { runtime_unlock(&prof); return; } - n = runtime_callers(0, prof.locbuf, nelem(prof.locbuf)); - for(i = 0; i < n; i++) - prof.pcbuf[i] = prof.locbuf[i].pc; - if(n > 0) - prof.fn(prof.pcbuf, n); + n = 0; + if(traceback) { + n = runtime_callers(0, prof.locbuf, nelem(prof.locbuf)); + for(i = 0; i < n; i++) + prof.pcbuf[i] = prof.locbuf[i].pc; + } + if (!traceback || n <= 0) { + n = 2; + prof.pcbuf[0] = (uintptr)runtime_getcallerpc(&n); + prof.pcbuf[1] = (uintptr)System + 1; + } + prof.fn(prof.pcbuf, n); runtime_unlock(&prof); } @@ -2264,7 +2477,11 @@ runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz) if(fn == nil) hz = 0; - // Stop profiler on this cpu so that it is safe to lock prof. + // Disable preemption, otherwise we can be rescheduled to another thread + // that has profiling enabled. + m->locks++; + + // Stop profiler on this thread so that it is safe to lock prof. // if a profiling signal came in while we had prof locked, // it would deadlock. runtime_resetcpuprofiler(0); @@ -2279,6 +2496,8 @@ runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz) if(hz != 0) runtime_resetcpuprofiler(hz); + + m->locks--; } // Change number of processors. The world is stopped, sched is locked. @@ -2296,7 +2515,8 @@ procresize(int32 new) for(i = 0; i < new; i++) { p = runtime_allp[i]; if(p == nil) { - p = (P*)runtime_mallocgc(sizeof(*p), 0, 0, 1); + p = (P*)runtime_mallocgc(sizeof(*p), 0, FlagNoInvokeGC); + p->id = i; p->status = Pgcstop; runtime_atomicstorep(&runtime_allp[i], p); } @@ -2308,7 +2528,7 @@ procresize(int32 new) } if(p->runq == nil) { p->runqsize = 128; - p->runq = (G**)runtime_mallocgc(p->runqsize*sizeof(G*), 0, 0, 1); + p->runq = (G**)runtime_mallocgc(p->runqsize*sizeof(G*), 0, FlagNoInvokeGC); } } @@ -2351,7 +2571,6 @@ procresize(int32 new) p->status = Pidle; pidleput(p); } - runtime_singleproc = new == 1; runtime_atomicstore((uint32*)&runtime_gomaxprocs, new); } @@ -2393,10 +2612,10 @@ releasep(void) } static void -inclocked(int32 v) +incidlelocked(int32 v) { runtime_lock(&runtime_sched); - runtime_sched.mlocked += v; + runtime_sched.nmidlelocked += v; if(v > 0) checkdead(); runtime_unlock(&runtime_sched); @@ -2411,12 +2630,12 @@ checkdead(void) int32 run, grunning, s; // -1 for sysmon - run = runtime_sched.mcount - runtime_sched.nmidle - runtime_sched.mlocked - 1 - countextra(); + run = runtime_sched.mcount - runtime_sched.nmidle - runtime_sched.nmidlelocked - 1 - countextra(); if(run > 0) return; if(run < 0) { - runtime_printf("checkdead: nmidle=%d mlocked=%d mcount=%d\n", - runtime_sched.nmidle, runtime_sched.mlocked, runtime_sched.mcount); + runtime_printf("checkdead: nmidle=%d nmidlelocked=%d mcount=%d\n", + runtime_sched.nmidle, runtime_sched.nmidlelocked, runtime_sched.mcount); runtime_throw("checkdead: inconsistent counts"); } grunning = 0; @@ -2441,10 +2660,10 @@ static void sysmon(void) { uint32 idle, delay; - int64 now, lastpoll; + int64 now, lastpoll, lasttrace; G *gp; - uint32 ticks[MaxGomaxprocs]; + lasttrace = 0; idle = 0; // how many cycles in succession we had not wokeup somebody delay = 0; for(;;) { @@ -2455,9 +2674,10 @@ sysmon(void) if(delay > 10*1000) // up to 10ms delay = 10*1000; runtime_usleep(delay); - if(runtime_gcwaiting || runtime_atomicload(&runtime_sched.npidle) == (uint32)runtime_gomaxprocs) { // TODO: fast atomic + if(runtime_debug.schedtrace <= 0 && + (runtime_sched.gcwaiting || runtime_atomicload(&runtime_sched.npidle) == (uint32)runtime_gomaxprocs)) { // TODO: fast atomic runtime_lock(&runtime_sched); - if(runtime_atomicload(&runtime_gcwaiting) || runtime_atomicload(&runtime_sched.npidle) == (uint32)runtime_gomaxprocs) { + if(runtime_atomicload(&runtime_sched.gcwaiting) || runtime_atomicload(&runtime_sched.npidle) == (uint32)runtime_gomaxprocs) { runtime_atomicstore(&runtime_sched.sysmonwait, 1); runtime_unlock(&runtime_sched); runtime_notesleep(&runtime_sched.sysmonnote); @@ -2470,53 +2690,198 @@ sysmon(void) // poll network if not polled for more than 10ms lastpoll = runtime_atomicload64(&runtime_sched.lastpoll); now = runtime_nanotime(); - if(lastpoll != 0 && lastpoll + 10*1000*1000 > now) { + if(lastpoll != 0 && lastpoll + 10*1000*1000 < now) { + runtime_cas64(&runtime_sched.lastpoll, lastpoll, now); gp = runtime_netpoll(false); // non-blocking - injectglist(gp); + if(gp) { + // Need to decrement number of idle locked M's + // (pretending that one more is running) before injectglist. + // Otherwise it can lead to the following situation: + // injectglist grabs all P's but before it starts M's to run the P's, + // another M returns from syscall, finishes running its G, + // observes that there is no work to do and no other running M's + // and reports deadlock. + incidlelocked(-1); + injectglist(gp); + incidlelocked(1); + } } // retake P's blocked in syscalls - if(retake(ticks)) + // and preempt long running G's + if(retake(now)) idle = 0; else idle++; + + if(runtime_debug.schedtrace > 0 && lasttrace + runtime_debug.schedtrace*1000000ll <= now) { + lasttrace = now; + runtime_schedtrace(runtime_debug.scheddetail); + } } } +typedef struct Pdesc Pdesc; +struct Pdesc +{ + uint32 schedtick; + int64 schedwhen; + uint32 syscalltick; + int64 syscallwhen; +}; +static Pdesc pdesc[MaxGomaxprocs]; + static uint32 -retake(uint32 *ticks) +retake(int64 now) { uint32 i, s, n; int64 t; P *p; + Pdesc *pd; n = 0; for(i = 0; i < (uint32)runtime_gomaxprocs; i++) { p = runtime_allp[i]; if(p==nil) continue; - t = p->tick; - if(ticks[i] != t) { - ticks[i] = t; - continue; - } + pd = &pdesc[i]; s = p->status; - if(s != Psyscall) - continue; - if(p->runqhead == p->runqtail && runtime_atomicload(&runtime_sched.nmspinning) + runtime_atomicload(&runtime_sched.npidle) > 0) // TODO: fast atomic - continue; - // Need to increment number of locked M's before the CAS. - // Otherwise the M from which we retake can exit the syscall, - // increment nmidle and report deadlock. - inclocked(-1); - if(runtime_cas(&p->status, s, Pidle)) { - n++; - handoffp(p); + if(s == Psyscall) { + // Retake P from syscall if it's there for more than 1 sysmon tick (20us). + // But only if there is other work to do. + t = p->syscalltick; + if(pd->syscalltick != t) { + pd->syscalltick = t; + pd->syscallwhen = now; + continue; + } + if(p->runqhead == p->runqtail && + runtime_atomicload(&runtime_sched.nmspinning) + runtime_atomicload(&runtime_sched.npidle) > 0) + continue; + // Need to decrement number of idle locked M's + // (pretending that one more is running) before the CAS. + // Otherwise the M from which we retake can exit the syscall, + // increment nmidle and report deadlock. + incidlelocked(-1); + if(runtime_cas(&p->status, s, Pidle)) { + n++; + handoffp(p); + } + incidlelocked(1); + } else if(s == Prunning) { + // Preempt G if it's running for more than 10ms. + t = p->schedtick; + if(pd->schedtick != t) { + pd->schedtick = t; + pd->schedwhen = now; + continue; + } + if(pd->schedwhen + 10*1000*1000 > now) + continue; + // preemptone(p); } - inclocked(1); } return n; } +// Tell all goroutines that they have been preempted and they should stop. +// This function is purely best-effort. It can fail to inform a goroutine if a +// processor just started running it. +// No locks need to be held. +// Returns true if preemption request was issued to at least one goroutine. +static bool +preemptall(void) +{ + return false; +} + +void +runtime_schedtrace(bool detailed) +{ + static int64 starttime; + int64 now; + int64 id1, id2, id3; + int32 i, q, t, h, s; + const char *fmt; + M *mp, *lockedm; + G *gp, *lockedg; + P *p; + + now = runtime_nanotime(); + if(starttime == 0) + starttime = now; + + runtime_lock(&runtime_sched); + runtime_printf("SCHED %Dms: gomaxprocs=%d idleprocs=%d threads=%d idlethreads=%d runqueue=%d", + (now-starttime)/1000000, runtime_gomaxprocs, runtime_sched.npidle, runtime_sched.mcount, + runtime_sched.nmidle, runtime_sched.runqsize); + if(detailed) { + runtime_printf(" gcwaiting=%d nmidlelocked=%d nmspinning=%d stopwait=%d sysmonwait=%d\n", + runtime_sched.gcwaiting, runtime_sched.nmidlelocked, runtime_sched.nmspinning, + runtime_sched.stopwait, runtime_sched.sysmonwait); + } + // We must be careful while reading data from P's, M's and G's. + // Even if we hold schedlock, most data can be changed concurrently. + // E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil. + for(i = 0; i < runtime_gomaxprocs; i++) { + p = runtime_allp[i]; + if(p == nil) + continue; + mp = p->m; + t = p->runqtail; + h = p->runqhead; + s = p->runqsize; + q = t - h; + if(q < 0) + q += s; + if(detailed) + runtime_printf(" P%d: status=%d schedtick=%d syscalltick=%d m=%d runqsize=%d/%d gfreecnt=%d\n", + i, p->status, p->schedtick, p->syscalltick, mp ? mp->id : -1, q, s, p->gfreecnt); + else { + // In non-detailed mode format lengths of per-P run queues as: + // [len1 len2 len3 len4] + fmt = " %d"; + if(runtime_gomaxprocs == 1) + fmt = " [%d]\n"; + else if(i == 0) + fmt = " [%d"; + else if(i == runtime_gomaxprocs-1) + fmt = " %d]\n"; + runtime_printf(fmt, q); + } + } + if(!detailed) { + runtime_unlock(&runtime_sched); + return; + } + for(mp = runtime_allm; mp; mp = mp->alllink) { + p = mp->p; + gp = mp->curg; + lockedg = mp->lockedg; + id1 = -1; + if(p) + id1 = p->id; + id2 = -1; + if(gp) + id2 = gp->goid; + id3 = -1; + if(lockedg) + id3 = lockedg->goid; + runtime_printf(" M%d: p=%D curg=%D mallocing=%d throwing=%d gcing=%d" + " locks=%d dying=%d helpgc=%d spinning=%d lockedg=%D\n", + mp->id, id1, id2, + mp->mallocing, mp->throwing, mp->gcing, mp->locks, mp->dying, mp->helpgc, + mp->spinning, id3); + } + for(gp = runtime_allg; gp; gp = gp->alllink) { + mp = gp->m; + lockedm = gp->lockedm; + runtime_printf(" G%D: status=%d(%s) m=%d lockedm=%d\n", + gp->goid, gp->status, gp->waitreason, mp ? mp->id : -1, + lockedm ? lockedm->id : -1); + } + runtime_unlock(&runtime_sched); +} + // Put mp on midle list. // Sched must be locked. static void @@ -2559,7 +2924,7 @@ globrunqput(G *gp) // Try get a batch of G's from the global runnable queue. // Sched must be locked. static G* -globrunqget(P *p) +globrunqget(P *p, int32 max) { G *gp, *gp1; int32 n; @@ -2569,6 +2934,8 @@ globrunqget(P *p) n = runtime_sched.runqsize/runtime_gomaxprocs+1; if(n > runtime_sched.runqsize) n = runtime_sched.runqsize; + if(max > 0 && n > max) + n = max; runtime_sched.runqsize -= n; if(runtime_sched.runqsize == 0) runtime_sched.runqtail = nil; @@ -2827,6 +3194,22 @@ runtime_testSchedLocalQueueSteal(void) } } +intgo runtime_debug_setMaxThreads(intgo) + __asm__(GOSYM_PREFIX "runtime_debug.setMaxThreads"); + +intgo +runtime_debug_setMaxThreads(intgo in) +{ + intgo out; + + runtime_lock(&runtime_sched); + out = runtime_sched.maxmcount; + runtime_sched.maxmcount = in; + checkmcount(); + runtime_unlock(&runtime_sched); + return out; +} + void runtime_proc_scan(void (*addroot)(Obj)) { @@ -2852,3 +3235,11 @@ __go_get_closure(void) { return g->closure; } + +// Return whether we are waiting for a GC. This gc toolchain uses +// preemption instead. +bool +runtime_gcwaiting(void) +{ + return runtime_sched.gcwaiting; +} diff --git a/libgo/runtime/race.h b/libgo/runtime/race.h index 3357bed..884245c 100644 --- a/libgo/runtime/race.h +++ b/libgo/runtime/race.h @@ -16,14 +16,14 @@ uintptr runtime_raceinit(void); void runtime_racefini(void); void runtime_racemapshadow(void *addr, uintptr size); -void runtime_racemalloc(void *p, uintptr sz, void *pc); +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); void runtime_racereadpc(void *addr, void *callpc, void *pc); -void runtime_racewriterangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc); -void runtime_racereadrangepc(void *addr, uintptr sz, uintptr step, void *callpc, void *pc); +void runtime_racewriterangepc(void *addr, uintptr sz, void *callpc, void *pc); +void runtime_racereadrangepc(void *addr, uintptr sz, void *callpc, void *pc); void runtime_racefingo(void); void runtime_raceacquire(void *addr); void runtime_raceacquireg(G *gp, void *addr); diff --git a/libgo/runtime/runtime.c b/libgo/runtime/runtime.c index 1ff6d00..56fc045 100644 --- a/libgo/runtime/runtime.c +++ b/libgo/runtime/runtime.c @@ -124,11 +124,12 @@ TestAtomic64(void) z64 = 42; x64 = 0; PREFETCH(&z64); - if(runtime_cas64(&z64, &x64, 1)) + if(runtime_cas64(&z64, x64, 1)) runtime_throw("cas64 failed"); - if(x64 != 42) + if(x64 != 0) runtime_throw("cas64 failed"); - if(!runtime_cas64(&z64, &x64, 1)) + x64 = 42; + if(!runtime_cas64(&z64, x64, 1)) runtime_throw("cas64 failed"); if(x64 != 42 || z64 != 1) runtime_throw("cas64 failed"); @@ -279,3 +280,79 @@ runtime_signalstack(byte *p, int32 n) if(sigaltstack(&st, nil) < 0) *(int *)0xf1 = 0xf1; } + +DebugVars runtime_debug; + +static struct { + const char* name; + int32* value; +} dbgvar[] = { + {"gctrace", &runtime_debug.gctrace}, + {"schedtrace", &runtime_debug.schedtrace}, + {"scheddetail", &runtime_debug.scheddetail}, +}; + +void +runtime_parsedebugvars(void) +{ + const byte *p; + intgo i, n; + + p = runtime_getenv("GODEBUG"); + if(p == nil) + return; + for(;;) { + for(i=0; i<(intgo)nelem(dbgvar); i++) { + n = runtime_findnull((const byte*)dbgvar[i].name); + if(runtime_mcmp(p, dbgvar[i].name, n) == 0 && p[n] == '=') + *dbgvar[i].value = runtime_atoi(p+n+1); + } + p = (const byte *)runtime_strstr((const char *)p, ","); + if(p == nil) + break; + p++; + } +} + +// Poor mans 64-bit division. +// This is a very special function, do not use it if you are not sure what you are doing. +// int64 division is lowered into _divv() call on 386, which does not fit into nosplit functions. +// Handles overflow in a time-specific manner. +int32 +runtime_timediv(int64 v, int32 div, int32 *rem) +{ + int32 res, bit; + + if(v >= (int64)div*0x7fffffffLL) { + if(rem != nil) + *rem = 0; + return 0x7fffffff; + } + res = 0; + for(bit = 30; bit >= 0; bit--) { + if(v >= ((int64)div<<bit)) { + v = v - ((int64)div<<bit); + res += 1<<bit; + } + } + if(rem != nil) + *rem = v; + return res; +} + +// Setting the max stack size doesn't really do anything for gccgo. + +uintptr runtime_maxstacksize = 1<<20; // enough until runtime.main sets it for real + +intgo runtime_debug_setMaxStack(intgo) + __asm__ (GOSYM_PREFIX "runtime_debug.setMaxStack"); + +intgo +runtime_debug_setMaxStack(intgo in) +{ + intgo out; + + out = runtime_maxstacksize; + runtime_maxstacksize = in; + return out; +} diff --git a/libgo/runtime/runtime.h b/libgo/runtime/runtime.h index d2e7d4c..e82e832 100644 --- a/libgo/runtime/runtime.h +++ b/libgo/runtime/runtime.h @@ -72,6 +72,7 @@ typedef struct ParFor ParFor; typedef struct ParForThread ParForThread; typedef struct CgoMal CgoMal; typedef struct PollDesc PollDesc; +typedef struct DebugVars DebugVars; typedef struct __go_open_array Slice; typedef struct __go_interface Iface; @@ -82,6 +83,7 @@ typedef struct __go_panic_stack Panic; typedef struct __go_ptr_type PtrType; typedef struct __go_func_type FuncType; +typedef struct __go_interface_type InterfaceType; typedef struct __go_map_type MapType; typedef struct __go_channel_type ChanType; @@ -206,21 +208,20 @@ struct G void* param; // passed parameter on wakeup bool fromgogo; // reached from gogo int16 status; - int64 goid; uint32 selgen; // valid sudog pointer + int64 goid; const char* waitreason; // if status==Gwaiting G* schedlink; bool ispanic; bool issystem; // do not output in stack dump bool isbackground; // ignore in deadlock detector - bool blockingsyscall; // hint that the next syscall will block M* m; // for debuggers, but offset not hard-coded M* lockedm; int32 sig; int32 writenbuf; byte* writebuf; - // DeferChunk *dchunk; - // DeferChunk *dchunknext; + // DeferChunk* dchunk; + // DeferChunk* dchunknext; uintptr sigcode0; uintptr sigcode1; // uintptr sigpc; @@ -243,6 +244,7 @@ struct M size_t gsignalstacksize; void (*mstartfn)(void); G* curg; // current running goroutine + G* caughtsig; // goroutine running during fatal signal P* p; // attached P for executing Go code (nil if not executing Go code) P* nextp; int32 id; @@ -250,11 +252,9 @@ struct M int32 throwing; int32 gcing; int32 locks; - int32 nomemprof; int32 dying; int32 profilehz; int32 helpgc; - bool blockingsyscall; bool spinning; uint32 fastrand; uint64 ncgocall; // number of cgo calls in total @@ -289,10 +289,12 @@ struct P { Lock; - uint32 status; // one of Pidle/Prunning/... + int32 id; + uint32 status; // one of Pidle/Prunning/... P* link; - uint32 tick; // incremented on every scheduler or system call - M* m; // back-link to associated M (nil if idle) + uint32 schedtick; // incremented on every scheduler call + uint32 syscalltick; // incremented on every system call + M* m; // back-link to associated M (nil if idle) MCache* mcache; // Queue of runnable goroutines. @@ -308,9 +310,13 @@ struct P byte pad[64]; }; -// The m->locked word holds a single bit saying whether -// external calls to LockOSThread are in effect, and then a counter -// of the internal nesting depth of lockOSThread / unlockOSThread. +// The m->locked word holds two pieces of state counting active calls to LockOSThread/lockOSThread. +// The low bit (LockExternal) is a boolean reporting whether any LockOSThread call is active. +// External locks are not recursive; a second lock is silently ignored. +// The upper bits of m->lockedcount record the nesting depth of calls to lockOSThread +// (counting up by LockInternal), popped by unlockOSThread (counting down by LockInternal). +// Internal locks can be recursive. For instance, a lock for cgo can occur while the main +// goroutine is holding the lock during the initialization phase. enum { LockExternal = 1, @@ -333,19 +339,16 @@ enum SigIgnored = 1<<6, // the signal was ignored before we registered for it }; -#ifndef NSIG -#define NSIG 32 -#endif - -// NOTE(rsc): keep in sync with extern.go:/type.Func. -// Eventually, the loaded symbol table should be closer to this form. +// Layout of in-memory per-function information prepared by linker +// See http://golang.org/s/go12symtab. +// Keep in sync with linker and with ../../libmach/sym.c +// and with package debug/gosym. struct Func { String name; uintptr entry; // entry pc }; - #ifdef GOOS_windows enum { Windows = 1 @@ -372,7 +375,7 @@ struct Timers // If this struct changes, adjust ../time/sleep.go:/runtimeTimer. struct Timer { - int32 i; // heap index + int32 i; // heap index // Timer wakes up at when, and then at when+period, ... (period > 0 only) // each time calling f(now, arg) in the timer goroutine, so f must be @@ -420,6 +423,16 @@ struct CgoMal void *alloc; }; +// Holds variables parsed from GODEBUG env var. +struct DebugVars +{ + int32 gctrace; + int32 schedtrace; + int32 scheddetail; +}; + +extern bool runtime_precisestack; + /* * defined macros * you need super-gopher-guru privilege @@ -453,12 +466,11 @@ extern M* runtime_allm; extern P** runtime_allp; extern int32 runtime_gomaxprocs; extern uint32 runtime_needextram; -extern bool runtime_singleproc; extern uint32 runtime_panicking; -extern uint32 runtime_gcwaiting; // gc is waiting to run extern int8* runtime_goos; extern int32 runtime_ncpu; extern void (*runtime_sysargs)(int32, uint8**); +extern DebugVars runtime_debug; /* * common functions and data @@ -466,11 +478,13 @@ extern void (*runtime_sysargs)(int32, uint8**); #define runtime_strcmp(s1, s2) __builtin_strcmp((s1), (s2)) #define runtime_strstr(s1, s2) __builtin_strstr((s1), (s2)) intgo runtime_findnull(const byte*); +intgo runtime_findnullw(const uint16*); void runtime_dump(byte*, int32); /* * very low level c-called */ +void runtime_gogo(G*); struct __go_func_type; void runtime_args(int32, byte**); void runtime_osinit(); @@ -492,14 +506,13 @@ void runtime_sigenable(uint32 sig); void runtime_sigdisable(uint32 sig); int32 runtime_gotraceback(bool *crash); void runtime_goroutineheader(G*); -void runtime_goroutinetrailer(G*); void runtime_printtrace(Location*, int32, bool); #define runtime_open(p, f, m) open((p), (f), (m)) #define runtime_read(d, v, n) read((d), (v), (n)) #define runtime_write(d, v, n) write((d), (v), (n)) #define runtime_close(d) close(d) #define runtime_cas(pval, old, new) __sync_bool_compare_and_swap (pval, old, new) -#define runtime_cas64(pval, pold, new) __atomic_compare_exchange_n (pval, pold, new, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) +#define runtime_cas64(pval, old, new) __sync_bool_compare_and_swap (pval, old, new) #define runtime_casp(pval, old, new) __sync_bool_compare_and_swap (pval, old, new) // Don't confuse with XADD x86 instruction, // this one is actually 'addx', that is, add-and-fetch. @@ -530,17 +543,21 @@ void runtime_mallocinit(void); void runtime_mprofinit(void); #define runtime_malloc(s) __go_alloc(s) #define runtime_free(p) __go_free(p) -bool runtime_addfinalizer(void*, FuncVal *fn, const struct __go_func_type *); +bool runtime_addfinalizer(void*, FuncVal *fn, const struct __go_func_type *, const struct __go_ptr_type *); #define runtime_getcallersp(p) __builtin_frame_address(1) int32 runtime_mcount(void); int32 runtime_gcount(void); +void runtime_mcall(void(*)(G*)); uint32 runtime_fastrand1(void); +int32 runtime_timediv(int64, int32, int32*); void runtime_setmg(M*, G*); void runtime_newextram(void); #define runtime_exit(s) exit(s) #define runtime_breakpoint() __builtin_trap() void runtime_gosched(void); +void runtime_gosched0(G*); +void runtime_schedtrace(bool); void runtime_park(void(*)(Lock*), Lock*, const char*); void runtime_tsleep(int64, const char*); M* runtime_newm(void); @@ -555,6 +572,8 @@ int32 runtime_callers(int32, Location*, int32); int64 runtime_nanotime(void); void runtime_dopanic(int32) __attribute__ ((noreturn)); void runtime_startpanic(void); +void runtime_freezetheworld(void); +void runtime_unwindstack(G*, byte*); void runtime_sigprof(); void runtime_resetcpuprofiler(int32); void runtime_setcpuprofilerate(void(*)(uintptr*, int32), int32); @@ -567,10 +586,14 @@ void runtime_addtimer(Timer*); bool runtime_deltimer(Timer*); G* runtime_netpoll(bool); void runtime_netpollinit(void); -int32 runtime_netpollopen(int32, PollDesc*); -int32 runtime_netpollclose(int32); +int32 runtime_netpollopen(uintptr, PollDesc*); +int32 runtime_netpollclose(uintptr); void runtime_netpollready(G**, PollDesc*, int32); +uintptr runtime_netpollfd(PollDesc*); void runtime_crash(void); +void runtime_parsedebugvars(void); +void _rt0_go(void); +void* runtime_funcdata(Func*, int32); void runtime_stoptheworld(void); void runtime_starttheworld(void); @@ -603,11 +626,15 @@ void runtime_unlock(Lock*); * wake up early, it must wait to call noteclear until it * can be sure that no other goroutine is calling * notewakeup. + * + * notesleep/notetsleep are generally called on g0, + * notetsleepg is similar to notetsleep but is called on user g. */ void runtime_noteclear(Note*); void runtime_notesleep(Note*); void runtime_notewakeup(Note*); -void runtime_notetsleep(Note*, int64); +bool runtime_notetsleep(Note*, int64); // false - timeout +bool runtime_notetsleepg(Note*, int64); // false - timeout /* * low-level synchronization for implementing the above @@ -698,11 +725,13 @@ void runtime_newTypeAssertionError(const String*, const String*, const String*, __asm__ (GOSYM_PREFIX "runtime.NewTypeAssertionError"); void runtime_newErrorString(String, Eface*) __asm__ (GOSYM_PREFIX "runtime.NewErrorString"); +void runtime_newErrorCString(const char*, Eface*) + __asm__ (GOSYM_PREFIX "runtime.NewErrorCString"); /* * wrapped for go users */ -void runtime_semacquire(uint32 volatile *); +void runtime_semacquire(uint32 volatile *, bool); void runtime_semrelease(uint32 volatile *); int32 runtime_gomaxprocsfunc(int32 n); void runtime_procyield(uint32); @@ -711,19 +740,10 @@ void runtime_lockOSThread(void); void runtime_unlockOSThread(void); bool runtime_showframe(String, bool); +void runtime_printcreatedby(G*); uintptr runtime_memlimit(void); -// If appropriate, ask the operating system to control whether this -// thread should receive profiling signals. This is only necessary on OS X. -// An operating system should not deliver a profiling signal to a -// thread that is not actually executing (what good is that?), but that's -// what OS X prefers to do. When profiling is turned on, we mask -// away the profiling signal when threads go to sleep, so that OS X -// is forced to deliver the signal to a thread that's actually running. -// This is a no-op on other systems. -void runtime_setprof(bool); - #define ISNAN(f) __builtin_isnan(f) enum @@ -763,3 +783,6 @@ int32 getproccount(void); void __go_set_closure(void*); void* __go_get_closure(void); + +bool runtime_gcwaiting(void); +void runtime_badsignal(int); diff --git a/libgo/runtime/sema.goc b/libgo/runtime/sema.goc index be971bd..f5d5bc8 100644 --- a/libgo/runtime/sema.goc +++ b/libgo/runtime/sema.goc @@ -21,22 +21,23 @@ package sync #include "runtime.h" #include "arch.h" -typedef struct Sema Sema; -struct Sema +typedef struct SemaWaiter SemaWaiter; +struct SemaWaiter { uint32 volatile* addr; G* g; int64 releasetime; - Sema* prev; - Sema* next; + int32 nrelease; // -1 for acquire + SemaWaiter* prev; + SemaWaiter* next; }; typedef struct SemaRoot SemaRoot; struct SemaRoot { Lock; - Sema* head; - Sema* tail; + SemaWaiter* head; + SemaWaiter* tail; // Number of waiters. Read w/o the lock. uint32 volatile nwait; }; @@ -58,7 +59,7 @@ semroot(uint32 volatile *addr) } static void -semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s) +semqueue(SemaRoot *root, uint32 volatile *addr, SemaWaiter *s) { s->g = runtime_g(); s->addr = addr; @@ -72,7 +73,7 @@ semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s) } static void -semdequeue(SemaRoot *root, Sema *s) +semdequeue(SemaRoot *root, SemaWaiter *s) { if(s->next) s->next->prev = s->prev; @@ -97,10 +98,10 @@ cansemacquire(uint32 volatile *addr) return 0; } -static void -semacquireimpl(uint32 volatile *addr, int32 profile) +void +runtime_semacquire(uint32 volatile *addr, bool profile) { - Sema s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it + SemaWaiter s; // Needs to be allocated on stack, otherwise garbage collector could deallocate it SemaRoot *root; int64 t0; @@ -145,15 +146,9 @@ semacquireimpl(uint32 volatile *addr, int32 profile) } void -runtime_semacquire(uint32 volatile *addr) -{ - semacquireimpl(addr, 0); -} - -void runtime_semrelease(uint32 volatile *addr) { - Sema *s; + SemaWaiter *s; SemaRoot *root; root = semroot(addr); @@ -188,10 +183,117 @@ runtime_semrelease(uint32 volatile *addr) } } +// TODO(dvyukov): move to netpoll.goc once it's used by all OSes. +void net_runtime_Semacquire(uint32 *addr) + __asm__ (GOSYM_PREFIX "net.runtime_Semacquire"); + +void net_runtime_Semacquire(uint32 *addr) +{ + runtime_semacquire(addr, true); +} + +void net_runtime_Semrelease(uint32 *addr) + __asm__ (GOSYM_PREFIX "net.runtime_Semrelease"); + +void net_runtime_Semrelease(uint32 *addr) +{ + runtime_semrelease(addr); +} + func runtime_Semacquire(addr *uint32) { - semacquireimpl(addr, 1); + runtime_semacquire(addr, true); } func runtime_Semrelease(addr *uint32) { runtime_semrelease(addr); } + +typedef struct SyncSema SyncSema; +struct SyncSema +{ + Lock; + SemaWaiter* head; + SemaWaiter* tail; +}; + +func runtime_Syncsemcheck(size uintptr) { + if(size != sizeof(SyncSema)) { + runtime_printf("bad SyncSema size: sync:%D runtime:%D\n", (int64)size, (int64)sizeof(SyncSema)); + runtime_throw("bad SyncSema size"); + } +} + +// Syncsemacquire waits for a pairing Syncsemrelease on the same semaphore s. +func runtime_Syncsemacquire(s *SyncSema) { + SemaWaiter w, *wake; + int64 t0; + + w.g = runtime_g(); + w.nrelease = -1; + w.next = nil; + w.releasetime = 0; + t0 = 0; + if(runtime_blockprofilerate > 0) { + t0 = runtime_cputicks(); + w.releasetime = -1; + } + + runtime_lock(s); + if(s->head && s->head->nrelease > 0) { + // have pending release, consume it + wake = nil; + s->head->nrelease--; + if(s->head->nrelease == 0) { + wake = s->head; + s->head = wake->next; + if(s->head == nil) + s->tail = nil; + } + runtime_unlock(s); + if(wake) + runtime_ready(wake->g); + } else { + // enqueue itself + if(s->tail == nil) + s->head = &w; + else + s->tail->next = &w; + s->tail = &w; + runtime_park(runtime_unlock, s, "semacquire"); + if(t0) + runtime_blockevent(w.releasetime - t0, 2); + } +} + +// Syncsemrelease waits for n pairing Syncsemacquire on the same semaphore s. +func runtime_Syncsemrelease(s *SyncSema, n uint32) { + SemaWaiter w, *wake; + + w.g = runtime_g(); + w.nrelease = (int32)n; + w.next = nil; + w.releasetime = 0; + + runtime_lock(s); + while(w.nrelease > 0 && s->head && s->head->nrelease < 0) { + // have pending acquire, satisfy it + wake = s->head; + s->head = wake->next; + if(s->head == nil) + s->tail = nil; + if(wake->releasetime) + wake->releasetime = runtime_cputicks(); + runtime_ready(wake->g); + w.nrelease--; + } + if(w.nrelease > 0) { + // enqueue itself + if(s->tail == nil) + s->head = &w; + else + s->tail->next = &w; + s->tail = &w; + runtime_park(runtime_unlock, s, "semarelease"); + } else + runtime_unlock(s); +} diff --git a/libgo/runtime/signal_unix.c b/libgo/runtime/signal_unix.c index 5a506c8..ea0a58f 100644 --- a/libgo/runtime/signal_unix.c +++ b/libgo/runtime/signal_unix.c @@ -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 freebsd linux openbsd netbsd +// +build darwin dragonfly freebsd linux openbsd netbsd #include <sys/time.h> @@ -100,13 +100,11 @@ runtime_resetcpuprofiler(int32 hz) runtime_memclr((byte*)&it, sizeof it); if(hz == 0) { runtime_setitimer(ITIMER_PROF, &it, nil); - runtime_setprof(false); } else { it.it_interval.tv_sec = 0; it.it_interval.tv_usec = 1000000 / hz; it.it_value = it.it_interval; runtime_setitimer(ITIMER_PROF, &it, nil); - runtime_setprof(true); } runtime_m()->profilehz = hz; } diff --git a/libgo/runtime/sigqueue.goc b/libgo/runtime/sigqueue.goc index 8657216..6769b23 100644 --- a/libgo/runtime/sigqueue.goc +++ b/libgo/runtime/sigqueue.goc @@ -107,9 +107,7 @@ func signal_recv() (m uint32) { new = HASWAITER; if(runtime_cas(&sig.state, old, new)) { if (new == HASWAITER) { - runtime_entersyscallblock(); - runtime_notesleep(&sig); - runtime_exitsyscall(); + runtime_notetsleepg(&sig, -1); runtime_noteclear(&sig); } break; @@ -157,3 +155,10 @@ func signal_disable(s uint32) { sig.wanted[s/32] &= ~(1U<<(s&31)); runtime_sigdisable(s); } + +// This runs on a foreign stack, without an m or a g. No stack split. +void +runtime_badsignal(int sig) +{ + __go_sigsend(sig); +} diff --git a/libgo/runtime/string.goc b/libgo/runtime/string.goc index 64ed4f6e..a7446e9 100644 --- a/libgo/runtime/string.goc +++ b/libgo/runtime/string.goc @@ -21,6 +21,18 @@ runtime_findnull(const byte *s) return __builtin_strlen((const char*) s); } +intgo +runtime_findnullw(const uint16 *s) +{ + intgo l; + + if(s == nil) + return 0; + for(l=0; s[l]!=0; l++) + ; + return l; +} + static String gostringsize(intgo l, byte** pmem) { @@ -32,7 +44,7 @@ gostringsize(intgo l, byte** pmem) return runtime_emptystring; } // leave room for NUL for C runtime (e.g., callers of getenv) - mem = runtime_mallocgc(l+1, FlagNoPointers, 1, 0); + mem = runtime_mallocgc(l+1, 0, FlagNoScan|FlagNoZero); s.str = mem; s.len = l; mem[l] = 0; @@ -63,6 +75,15 @@ runtime_gostringnocopy(const byte *str) return s; } +String runtime_cstringToGo(byte*) + __asm__ (GOSYM_PREFIX "runtime.cstringToGo"); + +String +runtime_cstringToGo(byte *str) +{ + return runtime_gostringnocopy(str); +} + enum { Runeself = 0x80, diff --git a/libgo/runtime/thread-linux.c b/libgo/runtime/thread-linux.c index 13d23c4..ae56261 100644 --- a/libgo/runtime/thread-linux.c +++ b/libgo/runtime/thread-linux.c @@ -4,6 +4,7 @@ #include "runtime.h" #include "defs.h" +#include "signal_unix.h" // Linux futex. // @@ -33,25 +34,22 @@ typedef struct timespec Timespec; void runtime_futexsleep(uint32 *addr, uint32 val, int64 ns) { - Timespec ts, *tsp; - - if(ns < 0) - tsp = nil; - else { - ts.tv_sec = ns/1000000000LL; - ts.tv_nsec = ns%1000000000LL; - // Avoid overflow - if(ts.tv_sec > 1<<30) - ts.tv_sec = 1<<30; - tsp = &ts; - } + Timespec ts; + int32 nsec; // Some Linux kernels have a bug where futex of // FUTEX_WAIT returns an internal error code // as an errno. Libpthread ignores the return value // here, and so can we: as it says a few lines up, // spurious wakeups are allowed. - syscall(__NR_futex, addr, FUTEX_WAIT, val, tsp, nil, 0); + + if(ns < 0) { + syscall(__NR_futex, addr, FUTEX_WAIT, val, nil, nil, 0); + return; + } + ts.tv_sec = runtime_timediv(ns, 1000000000LL, &nsec); + ts.tv_nsec = nsec; + syscall(__NR_futex, addr, FUTEX_WAIT, val, &ts, nil, 0); } // If any procs are sleeping on addr, wake up at most cnt. diff --git a/libgo/runtime/time.goc b/libgo/runtime/time.goc index 8d12fe0..e4e35ec 100644 --- a/libgo/runtime/time.goc +++ b/libgo/runtime/time.goc @@ -12,8 +12,13 @@ package time #include "malloc.h" #include "race.h" +enum { + debug = 0, +}; + static Timers timers; static void addtimer(Timer*); +static void dumptimers(const char*); // Package time APIs. // Godoc uses the comments in package time, not these. @@ -92,6 +97,11 @@ addtimer(Timer *t) int32 n; Timer **nt; + // when must never be negative; otherwise timerproc will overflow + // during its delta calculation and never expire other timers. + if(t->when < 0) + t->when = (int64)((1ULL<<63)-1); + if(timers.len >= timers.cap) { // Grow slice. n = 16; @@ -121,8 +131,13 @@ addtimer(Timer *t) timers.timerproc = __go_go(timerproc, nil); timers.timerproc->issystem = true; } + if(debug) + dumptimers("addtimer"); } +// Used to force a dereference before the lock is acquired. +static int32 gi; + // Delete timer t from the heap. // Do not need to update the timerproc: // if it wakes up early, no big deal. @@ -131,6 +146,11 @@ runtime_deltimer(Timer *t) { int32 i; + // Dereference t so that any panic happens before the lock is held. + // Discard result, because t might be moving in the heap. + i = t->i; + gi = i; + runtime_lock(&timers); // t may not be registered anymore and may have @@ -152,6 +172,8 @@ runtime_deltimer(Timer *t) siftup(i); siftdown(i); } + if(debug) + dumptimers("deltimer"); runtime_unlock(&timers); return true; } @@ -170,6 +192,7 @@ timerproc(void* dummy __attribute__ ((unused))) for(;;) { runtime_lock(&timers); + timers.sleeping = false; now = runtime_nanotime(); for(;;) { if(timers.len == 0) { @@ -210,9 +233,7 @@ timerproc(void* dummy __attribute__ ((unused))) timers.sleeping = true; runtime_noteclear(&timers.waitnote); runtime_unlock(&timers); - runtime_entersyscallblock(); - runtime_notetsleep(&timers.waitnote, delta); - runtime_exitsyscall(); + runtime_notetsleepg(&timers.waitnote, delta); } } @@ -222,18 +243,20 @@ static void siftup(int32 i) { int32 p; + int64 when; Timer **t, *tmp; t = timers.t; + when = t[i]->when; + tmp = t[i]; while(i > 0) { - p = (i-1)/2; // parent - if(t[i]->when >= t[p]->when) + p = (i-1)/4; // parent + if(when >= t[p]->when) break; - tmp = t[i]; t[i] = t[p]; - t[p] = tmp; t[i]->i = i; - t[p]->i = p; + t[p] = tmp; + tmp->i = p; i = p; } } @@ -241,29 +264,61 @@ siftup(int32 i) static void siftdown(int32 i) { - int32 c, len; + int32 c, c3, len; + int64 when, w, w3; Timer **t, *tmp; t = timers.t; len = timers.len; + when = t[i]->when; + tmp = t[i]; for(;;) { - c = i*2 + 1; // left child + c = i*4 + 1; // left child + c3 = c + 2; // mid child if(c >= len) { break; } - if(c+1 < len && t[c+1]->when < t[c]->when) + w = t[c]->when; + if(c+1 < len && t[c+1]->when < w) { + w = t[c+1]->when; c++; - if(t[c]->when >= t[i]->when) + } + if(c3 < len) { + w3 = t[c3]->when; + if(c3+1 < len && t[c3+1]->when < w3) { + w3 = t[c3+1]->when; + c3++; + } + if(w3 < w) { + w = w3; + c = c3; + } + } + if(w >= when) break; - tmp = t[i]; t[i] = t[c]; - t[c] = tmp; t[i]->i = i; - t[c]->i = c; + t[c] = tmp; + tmp->i = c; i = c; } } +static void +dumptimers(const char *msg) +{ + Timer *t; + int32 i; + + runtime_printf("timers: %s\n", msg); + for(i = 0; i < timers.len; i++) { + t = timers.t[i]; + runtime_printf("\t%d\t%p:\ti %d when %D period %D fn %p\n", + i, t, t->i, t->when, t->period, t->fv->fn); + } + runtime_printf("\n"); +} + void runtime_time_scan(void (*addroot)(Obj)) { |