-a7bd9a33067b
+7ebbddd21330
The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources.
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
bufio.gox \
bytes.gox \
crypto.gox \
+ encoding.gox \
errors.gox \
expvar.gox \
flag.gox \
image/jpeg.gox \
image/png.gox
+toolexeclibgoimagecolordir = $(toolexeclibgoimagedir)/color
+
+toolexeclibgoimagecolor_DATA = \
+ image/color/palette.gox
+
toolexeclibgoindexdir = $(toolexeclibgodir)/index
toolexeclibgoindex_DATA = \
go_crypto_files = \
go/crypto/crypto.go
+go_encoding_files = \
+ go/encoding/encoding.go
+
go_errors_files = \
go/errors/errors.go
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
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
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
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
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 \
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 \
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
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
endif
endif
endif
+endif
if LIBGO_IS_LINUX
go_os_pipe_file = go/os/pipe_linux.go
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 \
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) \
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 \
go_testing_files = \
go/testing/allocs.go \
go/testing/benchmark.go \
+ go/testing/cover.go \
go/testing/example.go \
go/testing/testing.go
go_archive_zip_files = \
go/archive/zip/reader.go \
+ go/archive/zip/register.go \
go/archive/zip/struct.go \
go/archive/zip/writer.go
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 = \
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 = \
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 \
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 \
bytes.lo \
bytes/index.lo \
crypto.lo \
+ encoding.lo \
errors.lo \
expvar.lo \
flag.lo \
sort.lo \
strconv.lo \
strings.lo \
+ strings/index.lo \
sync.lo \
syscall.lo \
syscall/errno.lo \
net/http/httputil.lo \
net/http/pprof.lo \
image/color.lo \
+ image/color/palette.lo \
image/draw.lo \
image/gif.lo \
image/jpeg.lo \
@$(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)
$(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
@$(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)
$(BUILDGOX)
crypto.gox: crypto.lo
$(BUILDGOX)
+encoding.gox: encoding.lo
+ $(BUILDGOX)
errors.gox: errors.lo
$(BUILDGOX)
expvar.gox: expvar.lo
image/png.gox: image/png.lo
$(BUILDGOX)
+image/color/palette.gox: image/color/palette.lo
+ $(BUILDGOX)
+
index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX)
"$(DESTDIR)$(toolexeclibgohashdir)" \
"$(DESTDIR)$(toolexeclibgohtmldir)" \
"$(DESTDIR)$(toolexeclibgoimagedir)" \
+ "$(DESTDIR)$(toolexeclibgoimagecolordir)" \
"$(DESTDIR)$(toolexeclibgoindexdir)" \
"$(DESTDIR)$(toolexeclibgoiodir)" \
"$(DESTDIR)$(toolexeclibgologdir)" \
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 \
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 \
$(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=) \
# -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
bufio.gox \
bytes.gox \
crypto.gox \
+ encoding.gox \
errors.gox \
expvar.gox \
flag.gox \
image/jpeg.gox \
image/png.gox
+toolexeclibgoimagecolordir = $(toolexeclibgoimagedir)/color
+toolexeclibgoimagecolor_DATA = \
+ image/color/palette.gox
+
toolexeclibgoindexdir = $(toolexeclibgodir)/index
toolexeclibgoindex_DATA = \
index/suffixarray.gox
go_crypto_files = \
go/crypto/crypto.go
+go_encoding_files = \
+ go/encoding/encoding.go
+
go_errors_files = \
go/errors/errors.go
# 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 =
@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) \
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 \
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 \
@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
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 \
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) \
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 \
go_testing_files = \
go/testing/allocs.go \
go/testing/benchmark.go \
+ go/testing/cover.go \
go/testing/example.go \
go/testing/testing.go
go_archive_zip_files = \
go/archive/zip/reader.go \
+ go/archive/zip/register.go \
go/archive/zip/struct.go \
go/archive/zip/writer.go
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_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_rsa_files = \
go/crypto/rsa/pkcs1v15.go \
+ go/crypto/rsa/pss.go \
go/crypto/rsa/rsa.go
go_crypto_sha1_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 \
bytes.lo \
bytes/index.lo \
crypto.lo \
+ encoding.lo \
errors.lo \
expvar.lo \
flag.lo \
sort.lo \
strconv.lo \
strings.lo \
+ strings/index.lo \
sync.lo \
syscall.lo \
syscall/errno.lo \
net/http/httputil.lo \
net/http/pprof.lo \
image/color.lo \
+ image/color/palette.lo \
image/draw.lo \
image/gif.lo \
image/jpeg.lo \
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)"
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
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 \
uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \
uninstall-toolexeclibgoimageDATA \
+ uninstall-toolexeclibgoimagecolorDATA \
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \
uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \
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 \
uninstall-toolexeclibgohashDATA \
uninstall-toolexeclibgohtmlDATA \
uninstall-toolexeclibgoimageDATA \
+ uninstall-toolexeclibgoimagecolorDATA \
uninstall-toolexeclibgoindexDATA uninstall-toolexeclibgoioDATA \
uninstall-toolexeclibgologDATA uninstall-toolexeclibgomathDATA \
uninstall-toolexeclibgomimeDATA uninstall-toolexeclibgonetDATA \
@$(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)
$(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
@$(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)
$(BUILDGOX)
crypto.gox: crypto.lo
$(BUILDGOX)
+encoding.gox: encoding.lo
+ $(BUILDGOX)
errors.gox: errors.lo
$(BUILDGOX)
expvar.gox: expvar.lo
image/png.gox: image/png.lo
$(BUILDGOX)
+image/color/palette.gox: image/color/palette.lo
+ $(BUILDGOX)
+
index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX)
/* 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
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
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
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
is_linux=no
is_netbsd=no
is_openbsd=no
+is_dragonfly=no
is_rtems=no
is_solaris=no
GOOS=unknown
*-*-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
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='#'
;;
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"
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
is_linux=no
is_netbsd=no
is_openbsd=no
+is_dragonfly=no
is_rtems=no
is_solaris=no
GOOS=unknown
*-*-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
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)
;;
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
package tar
import (
+ "bytes"
"errors"
"fmt"
"os"
// 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.
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")
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()
+}
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
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 {
},
},
},
+ {
+ 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) {
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()
if hdr != nil || err != nil {
t.Errorf("test %d: Unexpected entry or error: hdr=%v err=%v", i, hdr, err)
}
- f.Close()
}
}
"bytes"
"io/ioutil"
"os"
+ "path"
"reflect"
+ "strings"
"testing"
"time"
)
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 {
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.
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.
}
// 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
}
}
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-- {
// 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
}
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
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.
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
}
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
}
// 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
// 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 {
}
}
+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]
}
}
}
+
+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")
+ }
+}
import (
"bufio"
- "compress/flate"
"encoding/binary"
"errors"
"hash"
"hash/crc32"
"io"
- "io/ioutil"
"os"
)
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) {
}
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)
// 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[:])
var rc *ReadCloser
rc, err = OpenReader(filepath.Join("testdata", zt.Name))
if err == nil {
+ defer rc.Close()
z = &rc.Reader
}
}
--- /dev/null
+// 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]
+}
import (
"os"
+ "path"
"time"
)
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)
// 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{
import (
"bufio"
- "compress/flate"
"encoding/binary"
"errors"
"hash"
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{
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)
}
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)
}
// 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)
}
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)
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)
}
}
}
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)
+ }
+}
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.
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.
// 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.
// 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.
// 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
// 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
}
} else {
n = copy(b.buf[b.n:], p)
b.n += n
- b.Flush()
+ b.flush()
}
nn += n
p = p[n:]
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
}
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()
b.n += n
nn += n
s = s[n:]
- b.Flush()
+ b.flush()
}
if b.err != nil {
return nn, b.err
}
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
}
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)
}
}
}
+// 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
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()
+ }
+}
"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)
// 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
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 {
// 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 {
"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
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
}
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
}
//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
i int
}
-var compareTests = []struct {
+var equalTests = []struct {
a, b []byte
i int
}{
{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)
}
}
-func TestEqual(t *testing.T) {
+func TestEqualExhaustive(t *testing.T) {
var size = 128
if testing.Short() {
size = 32
{"", "a", -1},
{"", "foo", -1},
{"fo", "foo", -1},
+ {"foo", "baz", -1},
{"foo", "foo", 0},
{"oofofoofooo", "f", 2},
{"oofofoofooo", "foo", 4},
}
}
+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
}
}
+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.
--- /dev/null
+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)))
+}
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;
+}
}
}
+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) {
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
}
// 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.
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)
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
}
}
if !bz2.setupDone {
- err = bz2.setup()
+ err = bz2.setup(true)
brErr := bz2.br.Err()
if brErr != nil {
err = brErr
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.
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")
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)
// 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++
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
+}
import (
"bytes"
+ "encoding/base64"
"encoding/hex"
"io"
"io/ioutil"
}
}
+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 {
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=
+`
// 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
// 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
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
}
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) {
// 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)
}
}
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)
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)
// 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
zw.Write(dict)
zw.Flush()
dw.enabled = true
+ zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method.
return zw, err
}
// 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
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)
+ }
+}
"fmt"
"io"
"io/ioutil"
+ "reflect"
"sync"
"testing"
)
}
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))
+}
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.")
+ }
+}
}
}
+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
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
// 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} }
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
// 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()
// 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 {
// 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
// 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
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
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
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
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
}
func benchmarkDecode(b *testing.B, testfile, level, n int) {
+ b.ReportAllocs()
b.StopTimer()
b.SetBytes(int64(n))
buf0, err := ioutil.ReadFile(testfiles[testfile])
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()
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)))
}
}
import (
"bytes"
"io"
+ "io/ioutil"
+ "os"
"testing"
+ "time"
)
type gunzipTest struct {
}
}
}
+
+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
+ }
+}
// 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
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).
}
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
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)
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
return nil
}
z.closed = true
- if z.compressor == nil {
+ if !z.wroteHeader {
z.Write(nil)
if z.err != nil {
return z.err
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())
+ }
+}
}, 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
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
}
}
}
+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)
}
}
+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
// 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
// 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
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
package heap
import (
+ "math/rand"
"testing"
)
}
}
}
+
+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)
+ }
+}
// 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
// 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
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
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)
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) {
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
+}
}
}
+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
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)
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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[:])
+ }
+}
// 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
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
}
import (
"hash"
+ "strconv"
)
// Hash identifies a cryptographic hash function that is implemented in another
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.
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{}
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
}
// 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.
// 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 {
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) {
}
}
-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)
+ }
}
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
}
var initonce sync.Once
-var p256 *CurveParams
var p384 *CurveParams
var p521 *CurveParams
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)
}
}
+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
}
}
+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)
--- /dev/null
+// 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
+}
// DO NOT EDIT.
// Generate with: go run gen.go{{if .Full}} -full{{end}} | gofmt >md5block.go
-// +build !amd64
+// +build !amd64,!386,!arm
package md5
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
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()
}
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++ {
}
}
-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()
// DO NOT EDIT.
// Generate with: go run gen.go -full | gofmt >md5block.go
-// +build !amd64,!386
+// +build !amd64,!386,!arm
package md5
// 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)
// 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)
+}
// 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.
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
}
}
}
+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
--- /dev/null
+// 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())
+}
--- /dev/null
+// 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
+}
// Package rsa implements RSA encryption as specified in PKCS#1.
package rsa
-// TODO(agl): Add support for PSS padding.
-
import (
"crypto/rand"
"crypto/subtle"
}
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
}
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 {
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()
}
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 {
}
}
-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)
package sha1
+//go:noescape
+
func block(dig *digest, p []byte)
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 {
}
h := d.h[:]
- size := Size
if d.is224 {
h = d.h[:7]
- size = Size224
}
var digest [Size]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
}
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 {
}
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 {
// 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
}
h := d.h[:]
- size := Size
if d.is384 {
h = d.h[:6]
- size = Size384
}
var digest [Size]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
}
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 {
}
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 {
}
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)
+}
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)
+ }
+ }
+}
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 {
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{} {
// 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)),
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
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
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()
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
// 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
)
"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.
// 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
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
// 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
}
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.
// 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
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 {
hc.mac = hc.nextMac
hc.nextCipher = nil
hc.nextMac = nil
+ for i := range hc.seq {
+ hc.seq[i] = 0
+ }
return nil
}
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:]
}
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
// 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.
}
// 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))
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")
}
n := len(b.data) - recordHeaderLen
b.data[3] = byte(n >> 8)
b.data[4] = byte(n)
+ hc.incSeq()
return true, 0
}
// 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)
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
case typeCertificate:
m = new(certificateMsg)
case typeCertificateRequest:
- m = new(certificateRequestMsg)
+ m = &certificateRequestMsg{
+ hasSignatureAndHash: c.vers >= VersionTLS12,
+ }
case typeCertificateStatus:
m = new(certificateStatusMsg)
case typeServerKeyExchange:
case typeClientKeyExchange:
m = new(clientKeyExchangeMsg)
case typeCertificateVerify:
- m = new(certificateVerifyMsg)
+ m = &certificateVerifyMsg{
+ hasSignatureAndHash: c.vers >= VersionTLS12,
+ }
case typeNextProtocol:
m = new(nextProtoMsg)
case typeFinished:
// 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 {
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.
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() {
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,
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)
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()
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)
}
}
}
- if _, ok := certs[0].PublicKey.(*rsa.PublicKey); !ok {
+ switch certs[0].PublicKey.(type) {
+ case *rsa.PublicKey, *ecdsa.PublicKey:
+ break
+ default:
return c.sendAlert(alertUnsupportedCertificate)
}
return err
}
- keyAgreement := suite.ka()
+ keyAgreement := suite.ka(c.vers)
skx, ok := msg.(*serverKeyExchangeMsg)
if ok {
}
}
- var certToSend *Certificate
+ var chainToSend *Certificate
var certRequested bool
certReq, ok := msg.(*certificateRequestMsg)
if ok {
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
}
}
// 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
+ }
+ }
}
}
// 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())
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)
}
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})
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 {
}
}
-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")
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
// 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,
},
}
-var ecdheAESClientScript = [][]byte{
+var ecdheRSAAESClientScript = [][]byte{
{
0x16, 0x03, 0x01, 0x00, 0x4a, 0x01, 0x00, 0x00,
0x46, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
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,
+ },
+}
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
+ signatureAndHashes []signatureAndHash
}
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 {
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
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
m.ocspStapling = false
m.ticketSupported = false
m.sessionTicket = nil
+ m.signatureAndHashes = nil
if len(data) == 0 {
// ClientHello is optionally followed by extension data
// 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:]
}
}
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
}
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) {
}
length += casLength
+ if m.hasSignatureAndHash {
+ length += 2 + 2*len(m.signatureAndHashes)
+ }
+
x = make([]byte, 4+length)
x[0] = typeCertificateRequest
x[1] = uint8(length >> 16)
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:]
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
}
}
type certificateVerifyMsg struct {
- raw []byte
- signature []byte
+ raw []byte
+ hasSignatureAndHash bool
+ signatureAndHash signatureAndHash
+ signature []byte
}
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)
}
// 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
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
}
}
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
+}
m.sessionTicket = randomBytes(rand.Intn(300), rand)
}
}
+ if rand.Intn(10) > 5 {
+ m.signatureAndHashes = supportedSKXSignatureAlgorithms
+ }
return reflect.ValueOf(m)
}
import (
"crypto"
+ "crypto/ecdsa"
"crypto/rsa"
"crypto/subtle"
"crypto/x509"
+ "encoding/asn1"
"errors"
"io"
)
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.
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)
}
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
}
}
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
}
}
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
}
}
// 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
}
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
}
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
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
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 {
// 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.
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())
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
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
// 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
}
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
// 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
}
// 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
import (
"bytes"
+ "crypto/ecdsa"
+ "crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
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) {
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() {
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))
}
}
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},
}
}
-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) {
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
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 {
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 {
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
}
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,
},
}
+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 {
// 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,
},
}
-var des3ServerScript = [][]byte{
+var rsaDES3ServerScript = [][]byte{
{
0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
0xc1, 0x03, 0x03, 0x50, 0xae, 0x5d, 0x38, 0xec,
},
}
-var aesServerScript = [][]byte{
+var rsaAESServerScript = [][]byte{
{
0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
0xc1, 0x03, 0x03, 0x50, 0xae, 0x5c, 0xe9, 0x5e,
},
}
+// 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,
},
}
-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,
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,
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,
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.
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,
+ },
+}
import (
"crypto"
+ "crypto/ecdsa"
"crypto/elliptic"
"crypto/md5"
"crypto/rsa"
"crypto/sha1"
+ "crypto/sha256"
"crypto/x509"
+ "encoding/asn1"
"errors"
"io"
"math/big"
}
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")
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:
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)
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")
}
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
}
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")
}
package tls
import (
+ "crypto"
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
+ "crypto/sha256"
"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
}
}
-// 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()
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
}
// 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]
}
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
// 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. */
}
// These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 `
var testKeysFromTests = []testKeysFromTest{
{
- versionTLS10,
+ VersionTLS10,
"0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5",
"4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558",
"4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db",
16,
},
{
- versionTLS10,
+ VersionTLS10,
"03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890",
"4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106",
"4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c",
16,
},
{
- versionTLS10,
+ VersionTLS10,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
16,
},
{
- versionSSL30,
+ VersionSSL30,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
// 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 (
}
// 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
}
}
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]
}
}
}
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)
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
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
}
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)
}
}
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)
}
// 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
// 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) {
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
return CertificateInvalidError{c, Expired}
default:
- return UnknownAuthorityError{c}
+ return UnknownAuthorityError{c, nil, nil}
}
}
return nil
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}
}
}
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
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
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
package x509
import (
+ "bytes"
"encoding/hex"
"testing"
)
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)
+ }
}
package x509
import (
+ "fmt"
"net"
"runtime"
"strings"
// 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.
}
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}
}
}
}
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 {
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 {
}
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
},
},
{
+ 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},
{"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) {
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 {
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
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
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-----`
}
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),
},
}
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.
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
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
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 {
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:
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
}
type UnhandledCriticalExtension struct{}
func (h UnhandledCriticalExtension) Error() string {
- return "unhandled critical extension"
+ return "x509: unhandled critical extension"
}
type basicConstraints 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 {
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{
}
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,
}
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
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:
}
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
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 {
}
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
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
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
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 {
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 {
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 {
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 {
n++
}
- if len(template.PermittedDNSDomains) > 0 {
+ if len(template.PermittedDNSDomains) > 0 &&
+ !oidInExtensions(oidExtensionNameConstraints, template.ExtraExtensions) {
ret[n].Id = oidExtensionNameConstraints
ret[n].Critical = template.PermittedDNSDomainsCritical
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) {
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
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" +
}
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"
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)
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)
}
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 {
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)
}
}
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)
}
}
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
}
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
}
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)
}
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")
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)
} 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
}
}
cursor := &rowsCursor{
- pos: -1,
- rows: mrows,
- cols: s.colName,
+ pos: -1,
+ rows: mrows,
+ cols: s.colName,
+ errPos: -1,
}
return cursor, nil
}
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.
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
}
//
// 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"
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
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) {
}
// 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 {
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
}
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.
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
}
// 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
}
// 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()
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
}
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.
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.
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
}
}
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
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
}
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
}
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
// 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")
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()
}
// 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
}
package sql
import (
+ "database/sql/driver"
+ "errors"
"fmt"
+ "math/rand"
"reflect"
"runtime"
"strings"
}
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()
}
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)
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)
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 {
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) {
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) {
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)
}
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)
}
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() {
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)
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")
}
}
}
-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
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)
}
}
package dwarf
-import "errors"
+import (
+ "errors"
+ "strconv"
+)
// a single entry's description: a sequence of attributes
type abbrev struct {
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:
// 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.
} else {
f, err = Open(tt.file)
}
+ defer f.Close()
if err != nil {
t.Errorf("cannot open file %s: %v", tt.file, err)
continue
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
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
// 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
+ }
}
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 != "" {
// 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
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
}
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
}
func TestPCLine(t *testing.T) {
- if !dotest() {
+ if !dotest(false) {
return
}
defer endtest()
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)
}
}
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)
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,
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
}
/*
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 {
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
}
var t Table
+ if pcln.isGo12() {
+ t.go12line = pcln
+ }
fname := make(map[uint16]string)
t.Syms = make([]Sym, 0, n)
nf := 0
}
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:]
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
}
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
}
// 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 {
* 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
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)
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.
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
// 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
// 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
}
} 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)
"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
{[]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{}},
}
{[]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)}},
}
}
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
}
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
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
}
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 {
if params.set {
if tag != tagSequence {
- return StructuralError{"Non sequence tagged as set"}
+ return StructuralError{"non sequence tagged as set"}
}
tag = tagSet
}
{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"},
{
"" +
// 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
// 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 (
// 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
}
// 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
}
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
}
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{}
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()
+}
// 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")
// 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
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 {
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
}
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",
},
},
{
- Name: "Issue 2366",
+ Name: "TrailingCommaIneffective1",
TrailingComma: true,
TrimLeadingSpace: true,
Input: "a,b,\nc,d,e",
},
},
{
- 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"},
+ },
},
}
--- /dev/null
+// 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
+}
// 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
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)
}
import (
"bytes"
+ "encoding"
"errors"
"io"
"math"
// 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)
}
// 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())
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)
}
// 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
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)
}
}
// 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
}
// 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)
}
// 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
}
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.
// 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.
// 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)
}
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 {
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:
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:
}
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)
}
}
}
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)
}
} else {
v = reflect.NewAt(rcvrType, p).Elem()
}
- state.dec.decodeGobDecoder(state, v)
+ state.dec.decodeGobDecoder(ut, state, v)
}
return &op, int(ut.indir)
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() {
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
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))
}
}
// 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
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
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
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
import (
"bytes"
+ "encoding"
"math"
"reflect"
"unsafe"
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
}
// 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
}
// 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)
}
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.
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:
// 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)
}
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).
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)
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) {
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.
}
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)
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))
import (
"bytes"
- "errors"
"io"
"reflect"
"sync"
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
// 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)
// 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
}
// 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 {
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 },
}
}
-// 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")
}
}
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) {
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
}
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 {
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 {
// 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)
}
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)
}
}
// 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)
}
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)
}
}
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)
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)
}
} 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
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)
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) {
// 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)
}
import (
"bytes"
- "fmt"
"io"
"os"
"runtime"
}
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")
}
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")
}
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)
+ }
}
package gob
import (
+ "encoding"
"errors"
"fmt"
"os"
// 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
}
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
// 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
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
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) {
// 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 {
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
}
// 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
}
}
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
}
package json
import (
+ "encoding"
"encoding/base64"
"errors"
"fmt"
// 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
// 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.
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.
// 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.
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 {
import (
"bytes"
+ "encoding"
"fmt"
"image"
"reflect"
x int
}
-var txType = reflect.TypeOf((*tx)(nil)).Elem()
-
// A type that can unmarshal itself.
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
}
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
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.
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},
{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,
}
}
+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)
+ }
}
}
}
}
+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
}
// 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()
}
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
}
}
}
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)
}
}
// 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) {
func TestStringKind(t *testing.T) {
type stringKind string
- type aMap map[stringKind]int
var m1, m2 map[stringKind]int
m1 = map[stringKind]int{
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)
+ }
+ }
+}
import (
"bytes"
+ "encoding"
"encoding/base64"
"math"
"reflect"
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 {
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:])
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
}
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)
}
}()
}
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 {
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
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
}
}
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
}
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
"math"
"reflect"
"testing"
+ "unicode"
)
type Optionals struct {
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)
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)
}
}
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)
+ }
+}
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 {
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
}
}
+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 {
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.
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
}
// 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
}
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)
+ }
+ }
+}
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 {
import (
"bufio"
"bytes"
+ "encoding"
"fmt"
"io"
"reflect"
"strconv"
"strings"
- "time"
)
const (
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.
// 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
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"
// (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.
// 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:]
}
EscapeText(p, []byte(url))
p.WriteString(`" `)
- return prefix, true
+ p.prefixes = append(p.prefixes, prefix)
+
+ return prefix
}
// deleteAttrPrefix removes an attribute name space prefix.
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
}
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
}
}
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
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()
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:
return err
}
}
- case reflect.Struct:
- if vf.Type() == timeType {
- Escape(p, []byte(vf.Interface().(time.Time).Format(time.RFC3339Nano)))
- }
}
continue
}
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
}
}
}
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] {
}
}
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
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
{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.
{
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{
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) {
X string `xml:"X>Y,attr"`
}
+type BadAttr struct {
+ Name []string `xml:"name,attr"`
+}
+
var marshalErrorTests = []struct {
Value interface{}
Err string
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 {
}
}
+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)
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)
+ }
+}
import (
"bytes"
+ "encoding"
"errors"
+ "fmt"
"reflect"
"strconv"
"strings"
- "time"
)
// BUG(rsc): Mapping between XML elements and data structures is inherently flawed:
// 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.
//
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.
}
}
- 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 (
v.Set(reflect.ValueOf(start.Name))
break
}
- if typ == timeType {
- saveData = v
- break
- }
sv = v
tinfo, err = getTypeInfo(typ)
// 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
}
}
}
}
+ 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
}
}
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()))
// 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 {
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
}
package xml
import (
+ "io"
"reflect"
"strings"
"testing"
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)
+ }
+}
import (
"bufio"
"bytes"
+ "errors"
"fmt"
"io"
"strconv"
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
// 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
// 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.
// 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
}
const (
stkStart = iota
stkNs
+ stkEOF
)
func (d *Decoder) push(kind int) *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)
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
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
}
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 {
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 | '_' | ':')
case '\r':
esc = esc_cr
default:
- if !isInCharacterRange(r) {
+ if !isInCharacterRange(r) || (r == 0xFFFD && width == 1) {
esc = esc_fffd
break
}
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.
"reflect"
"strings"
"testing"
+ "unicode/utf8"
)
const testInput = `
}
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)
}
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)
+ }
+}
// 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
-}
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 }
return err
}
+func (i *intValue) Get() interface{} { return int(*i) }
+
func (i *intValue) String() string { return fmt.Sprintf("%v", *i) }
// -- int64 Value
return err
}
+func (i *int64Value) Get() interface{} { return int64(*i) }
+
func (i *int64Value) String() string { return fmt.Sprintf("%v", *i) }
// -- uint Value
return err
}
+func (i *uintValue) Get() interface{} { return uint(*i) }
+
func (i *uintValue) String() string { return fmt.Sprintf("%v", *i) }
// -- uint64 Value
return err
}
+func (i *uint64Value) Get() interface{} { return uint64(*i) }
+
func (i *uint64Value) String() string { return fmt.Sprintf("%v", *i) }
// -- string Value
return nil
}
+func (s *stringValue) Get() interface{} { return string(*s) }
+
func (s *stringValue) String() string { return fmt.Sprintf("%s", *s) }
// -- float64 Value
return err
}
+func (f *float64Value) Get() interface{} { return float64(*f) }
+
func (f *float64Value) String() string { return fmt.Sprintf("%v", *f) }
// -- time.Duration Value
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.
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
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
// 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.
// 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.
// 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.
// 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
// 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.
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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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.
// 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
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
}
// 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
}
// 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)
}
}
-// 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
// 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.
}
}
+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 {
func TestParse(t *testing.T) {
ResetForTesting(func() { t.Error("bad parse") })
- testParse(CommandLine(), t)
+ testParse(CommandLine, t)
}
func TestFlagSetParse(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)
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
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
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
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,
var b byte
-var fmttests = []struct {
+var fmtTests = []struct {
fmt string
val interface{}
out string
{"%+.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)"},
{"% .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)"},
// 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)]"},
}
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"
}
}
+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("")
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")
}
}{
// 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) {
failed *bool
}
-func (r Recur) String() string {
+func (r *Recur) String() string {
if recurCount++; recurCount > 10 {
*r.failed = true
return "FAIL"
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")
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'
}
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
// 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--
}
i--
buf[i] = digits[ua]
- for i > 0 && prec > nByte-i {
+ for i > 0 && prec > len(buf)-i {
i--
buf[i] = '0'
}
// 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:
oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
+ case 'b':
+ f.fmt_fb32(r)
case 'e':
f.fmt_e32(r)
case 'E':
oldPlus := f.plus
for i := 0; ; i++ {
switch verb {
+ case 'b':
+ f.fmt_fb64(r)
case 'e':
f.fmt_e64(r)
case 'E':
// 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.
// 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
}
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.
return
}
p.buf = p.buf[:0]
- p.field = nil
+ p.arg = nil
p.value = reflect.Value{}
ppFree.put(p)
}
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.
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.
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.
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)
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('=')
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')
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')
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('}')
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(')')
}
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
}
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
// 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
}
}
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 {
// %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
}
}
// Some types can be done without reflection.
- switch f := field.(type) {
+ switch f := arg.(type) {
case bool:
p.fmtBool(f, verb)
case float32:
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:
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' {
// %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)
}
// 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
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)
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++
// Process one verb
i++
- // flags and widths
+
+ // Do we have flags?
p.fmt.clearflags()
F:
for ; i < end; i++ {
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
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)
}
}
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')
// 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
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
}
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
s.peekRune = -1
s.atEOF = false
s.limit = hugeWid
- s.fieldLimit = hugeWid
+ s.argLimit = hugeWid
s.maxWid = hugeWid
s.validSave = true
s.count = 0
if r == eof {
return
}
+ if r == '\r' && s.peek("\n") {
+ continue
+ }
if r == '\n' {
if stopAtNewline {
break
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")
}
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 {
}
s.error(err)
}
- return math.Ldexp(f, n)
+ return math.Ldexp(f, m)
}
f, err := strconv.ParseFloat(str, n)
if err != nil {
// 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
}
case 'A', 'B', 'C', 'D', 'E', 'F':
return 10 + digit - 'A'
}
- s.errorString("Scan: illegal hex digit")
+ s.errorString("illegal hex digit")
return 0
}
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)
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 {
return
}
- switch v := field.(type) {
+ switch v := arg.(type) {
case *bool:
*v = s.scanBool(verb)
case *complex64:
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() {
// 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)))
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())
}
}
}
// 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.
break
}
if !isSpace(r) {
- s.errorString("Scan: expected newline")
+ s.errorString("expected newline")
break
}
}
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:])
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")
float32Val float32
float64Val float64
stringVal string
- stringVal1 string
bytesVal []byte
runeVal rune
complex64Val complex64
{"-.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)},
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 "]"
}
// 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.
// 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
}
// 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
}
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)) }
}
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()
// ----------------------------------------------------------------------------
// 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) }
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)
}
)
//
// 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
// 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.
)
// 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)
// 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)
+ }
+ }
}
}
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.
}
// 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 {
}
}
}
+
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
if n.High != nil {
Walk(v, n.High)
}
+ if n.Max != nil {
+ Walk(v, n.Max)
+ }
case *TypeAssertExpr:
Walk(v, n.X)
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 {
// 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":
// 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
}
// 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,
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 != "" {
// 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
}
}
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)
}
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":
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)
}
}
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)
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)
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 {
//
// 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
// 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 {
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.
if len(cond) > 0 {
ok := false
for _, c := range cond {
- if ctxt.match(c) {
+ if ctxt.match(c, nil) {
ok = true
break
}
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)
}
}
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":
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
}
// !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.
// 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]
}
}
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
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) {
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)
+ }
}
}
// 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",
"hash/fnv",
"image",
"image/color",
+ "image/color/palette",
"reflect",
},
"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"},
// 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
"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"},
"crypto/sha512": {"L3"},
"CRYPTO": {
- "CRYPTO-SUPPORT",
"crypto/aes",
"crypto/des",
"crypto/hmac",
}
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 {
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.
// - 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,
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)
}
}
// 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
t.Funcs(template.FuncMap{
"node": nodeFmt,
"synopsis": synopsisFmt,
+ "indent": indentFmt,
})
return template.Must(t.ParseFiles(filepath.Join(dataDir, filename)))
}
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() &&
// 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,
}
`
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)
}
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)
// 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{
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 != ' ' {
// 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 ""
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
+
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
+
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
+
--- /dev/null
+//
+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.
+
--- /dev/null
+//
+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.
+
--- /dev/null
+//
+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.
+
--- /dev/null
+package bugpara
+
+// BUG(rsc): Sometimes bugs have multiple paragraphs.
+//
+// Like this one.
*/}}{{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
// 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().
// 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().
// 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().
// 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().
// 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().
// 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().
// 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().
// 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.
"\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",
}
"io/ioutil"
"os"
"path/filepath"
+ "strings"
)
// If src != nil, readSource converts src to a []byte if possible;
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
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
}
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
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
} 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 {
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 {
}
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:
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
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)
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
// 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()
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()) }
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)
`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) {
`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 '{'" */ ; {} };`,
`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) {
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
// 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)
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
}
+// 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
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
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
}
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}.
// 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
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")
}
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 {
// 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
// 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.
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 {
--- /dev/null
+// 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)
+ }
+ }
+}
import (
"bytes"
+ "errors"
+ "io/ioutil"
"testing"
"text/template/parse"
)
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)
+ }
+}
// 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
// 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()
{
`<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)"`,
},
},
{
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()))
+ }
+}
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 &&
"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)
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.
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'
}
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', ' ':
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
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
{
"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",
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)
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
+ }
}
}
'}': `\}`,
}
-// 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.
// 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
}
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
}
ret := &Template{
false,
text,
+ text.Tree,
t.nameSpace,
}
t.set[name] = ret
ret := &Template{
false,
textClone,
+ textClone.Tree,
&nameSpace{
set: make(map[string]*Template),
},
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,
}
}
tmpl := &Template{
false,
template.New(name),
+ nil,
&nameSpace{
set: make(map[string]*Template),
},
tmpl := &Template{
false,
t.text.New(name),
+ nil,
t.nameSpace,
}
tmpl.set[name] = tmpl
"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'
}
// 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 {
// 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
--- /dev/null
+// 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()
+}
--- /dev/null
+// 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},
+}
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
)
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)
}
}
// 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=
+`
// 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
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
(*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) {
}
// 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) {
}
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 {
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)
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)
}
}
}
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{}
+ }
+ }
+ }
+}
import (
"image"
"image/color"
+ "image/png"
+ "os"
"testing"
)
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
+ }
+ }
+ }
+ }
+}
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
// 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)
// 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)
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
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
}
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
// 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
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.
// 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
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)
"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{}
}
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
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 {
}
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.
}
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)
+ }
+}
--- /dev/null
+// 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},
+ })
+}
--- /dev/null
+// 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)
+ }
+}
}
}
-// 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
}
}
-// 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
}
}
-// 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
}
}
-// 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
}
}
-// 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
}
}
-// 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
}
}
-// 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
}
}
}
-// 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
}
}
}
-// 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()
}
}
-// 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.
// 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
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
// 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.
// 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)
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
case 2:
offset += s.limit
}
- if offset < s.base || offset > s.limit {
+ if offset < s.base {
return 0, errOffset
}
s.off = offset
}
}
+// 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)
}
}
-func TestSectionReader_ReadAt(tst *testing.T) {
+func TestSectionReader_ReadAt(t *testing.T) {
dat := "a long sample data, 1234567890"
tests := []struct {
data string
{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)
+ }
+}
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
}
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)
p.l.Lock()
defer p.l.Unlock()
+ if p.werr != nil {
+ err = ErrClosedPipe
+ return
+ }
p.data = b
p.rwait.Signal()
for {
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)
+ }
+}
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
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()
}
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)
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,
return err
}
-func (n netConn) close() error {
+func (n *netConn) close() error {
return n.conn.Close()
}
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)
}
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()
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
} 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)
}
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 {
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()
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)
// 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
}
}
}()
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()
if err != nil {
continue
} else {
- return netConn{conn}, nil
+ return &netConn{conn: conn, local: true}, nil
}
}
}
after appropriate range reduction.
*/
-// Asin returns the arcsine of x.
+// Asin returns the arcsine, in radians, of x.
//
// Special cases are:
// Asin(±0) = ±0
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
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
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()
// 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.
// 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
// 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 {
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)
}
}
+// 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
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)
}
}
}
}
// 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 {
// 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])
// 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 {
}
}
+// 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)
// 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:
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:
// 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"
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:
//
// 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:
//
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
return y
}
-// Sin returns the sine of x.
+// Sin returns the sine of the radian argument x.
//
// Special cases are:
// Sin(±0) = ±0
-5.38695755929454629881E7, //0xc189afe03cbe5a31
}
-// Tan returns the tangent of x.
+// Tan returns the tangent of the radian argument x.
//
// Special cases are:
// Tan(±0) = ±0
}
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
-}
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('\\')
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) {
}
}
-// 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 {
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.
--- /dev/null
+// 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",
+ }
+}
// 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
// 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
// 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
/*
// 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
/*
// 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
/*
// 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.
// 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
}
func cgoLookupPort(net, service string) (port int, err error, completed bool) {
+ acquireThread()
+ defer releaseThread()
+
var res *syscall.Addrinfo
var hints syscall.Addrinfo
}
func cgoLookupIPCNAME(name string) (addrs []IP, cname string, err error, completed bool) {
+ acquireThread()
+ defer releaseThread()
+
var res *syscall.Addrinfo
var hints syscall.Addrinfo
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))
}
}
func copyIP(x IP) IP {
+ if len(x) < 16 {
+ return x.To16()
+ }
y := make(IP, len(x))
copy(y, x)
return y
// 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.
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":
// 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:
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".
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.
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
}
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
}
}
package net
import (
+ "bytes"
"flag"
"fmt"
"io"
"os"
+ "os/exec"
"reflect"
"regexp"
"runtime"
+ "strconv"
+ "sync"
"testing"
"time"
)
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
}
}
+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")
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 {
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()
}
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)
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()
+ }
+ }
+}
// 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) {
"[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")
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]
// 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
}
}
last = c
}
+ if last == '-' || partlen > 63 {
+ return false
+ }
return ok
}
// 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.
package net
import (
+ "io"
"math/rand"
"sync"
"time"
// 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}
}
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 {
}
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
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
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
}
--- /dev/null
+// 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)
+ }
+}
// 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
package net
import (
+ "strings"
"testing"
)
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},
{"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) {
}
}
}
+
+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)
+ }
+ }
+ }
+}
+++ /dev/null
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build 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)) }
--- /dev/null
+// 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)
--- /dev/null
+// 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")
+ }
+}
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 {
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
}
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
+}
// 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
)
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)
}
}
func (pd *pollDesc) Close() {
+ if pd.runtimeCtx == 0 {
+ return
+ }
runtime_pollClose(pd.runtimeCtx)
+ pd.runtimeCtx = 0
}
func (pd *pollDesc) Lock() {
// 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 {
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 {
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)
+++ /dev/null
-// 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
-}
+++ /dev/null
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build 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)
- }
- }
-}
// 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 {
}
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
}
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,
}
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()
}
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}
}
}
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}
}
}
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}
}
}
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}
}
}
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}
}
}
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}
}
}
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
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
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
+}
// 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
"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
// 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
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 {
}
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
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)
}
}
}
// 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
}
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()
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
}
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)
}
// 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) {
// 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
)
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
closesocket(fd)
return nil, err
}
+ if err := netfd.init(); err != nil {
+ netfd.Close()
+ return nil, err
+ }
netfd.setAddr(laddr, raddr)
return netfd, nil
}
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)
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.
"REQUEST_URI": "/path?a=b",
"CONTENT_LENGTH": "123",
"CONTENT_TYPE": "text/xml",
- "HTTPS": "1",
"REMOTE_ADDR": "5.6.7.8",
}
req, err := RequestFromMap(env)
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",
// 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)
}
}
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 {
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 {
// 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 {
"bytes"
"crypto/tls"
"crypto/x509"
+ "encoding/base64"
"errors"
"fmt"
"io"
}
}
+// 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)
}
}
}
+
+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)
+ }
+}
import (
"bytes"
"fmt"
+ "log"
+ "net"
"strconv"
"strings"
"time"
// 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))
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 {
},
{
&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) {
}
}
}
+
+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)
+ }
+ }
+}
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
}
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, ":")
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
}
// 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).
//
/*
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/")
...
// 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!")
+ })
+}
return newLoggingConn(baseName, c)
}
+var ExportAppendTime = appendTime
+
func (t *Transport) NumPendingRequestsForTesting() int {
t.reqMu.Lock()
defer t.reqMu.Unlock()
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
//
// 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
}
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)
}
}
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.
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
}
// 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.
"os/exec"
"path"
"path/filepath"
+ "reflect"
"regexp"
"runtime"
+ "strconv"
"strings"
"testing"
"time"
start, end int64 // range [start,end)
}
+var itoa = strconv.Itoa
+
var ServeFileRangeTests = []struct {
r string
code int
{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) {
}
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")
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) {
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
},
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"`,
},
}
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,
panic(err)
}
}
+
+type panicOnSeek struct{ io.ReadSeeker }
// 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.
}
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")
testHeader.WriteSubset(&buf, nil)
})
if n > 0 {
- t.Errorf("mallocs = %d; want 0", n)
+ t.Errorf("mallocs = %g; want 0", n)
}
}
"bufio"
"bytes"
"crypto/tls"
- "encoding/base64"
"errors"
"fmt"
"io"
// 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.
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 ||
// 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 {
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.
}
// 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 {
// 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.
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)
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
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()
}
}
-// 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 ||
"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) {
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)
+ }
+}
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++ {
}
}
+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"}
}
}
-// 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()
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 {
}
}
+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
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{
// 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)
},
}
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)
}
}
}
}
}
+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")
}
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")
// $ 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"))
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{
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{
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{
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,
"log"
"net"
"net/url"
+ "os"
"path"
"runtime"
"strconv"
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
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 {
// 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")
}
}
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
}
}
-// 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)
}
// 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
}
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
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:
}
}
}
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)
// 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.
contentLength: -1,
}
w.cw.res = w
- w.w, w.sw = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
+ w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize)
return w, nil
}
// 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)
}
}
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
// 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
// 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"
}
} 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")
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)
}
if !w.wroteHeader {
panic("")
}
- return w.status != StatusNotModified && w.req.Method != "HEAD"
+ return w.status != StatusNotModified
}
// The Life Of A Write is like this:
// 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
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() {
}
w.w.Flush()
- putBufioWriter(w.w, w.sw)
+ putBufioWriter(w.w)
w.cw.close()
w.conn.buf.Flush()
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
}
// 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
}
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.
// 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)
// 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
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
}
}
// 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
}
}
// 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
+++ /dev/null
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package http_test
-
-import (
- . "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)
- }
-}
"log"
. "net/http"
"net/http/httptest"
+ "reflect"
"strconv"
"strings"
"testing"
}
}
+// 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)
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 {
// msg is *Request or *Response.
func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
- t := &transferReader{}
+ t := &transferReader{RequestMethod: "GET"}
// Unify input
isResponse := false
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
// 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")
}
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:
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
}
}
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
}
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
}
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.
"bufio"
"compress/gzip"
"crypto/tls"
- "encoding/base64"
"errors"
"fmt"
"io"
}
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 {
t.idleMu.Lock()
m := t.idleConn
t.idleConn = nil
+ t.idleConnCh = nil
t.idleMu.Unlock()
if m == nil {
return
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 ""
}
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
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)
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()
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
}
}
// 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")
}
"io"
"io/ioutil"
"net"
+ "net/http"
. "net/http"
"net/http/httptest"
"net/url"
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)
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)
+ }
}
}
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)
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
}
nhigh := runtime.NumGoroutine()
tr.CloseIdleConnections()
- time.Sleep(50 * time.Millisecond)
+ time.Sleep(400 * time.Millisecond)
runtime.GC()
nfinal := runtime.NumGoroutine()
}
}
+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,
}
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") ||
// 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
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+// interfaceMulticastAddrTable returns addresses for a specific
+// interface.
+func interfaceMulticastAddrTable(ifi *Interface) ([]Addr, error) {
+ // TODO(mikio): Implement this like other platforms.
+ return nil, nil
+}
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)
}
package net
+import "errors"
+
// IP address lengths (bytes).
const (
IPv4len = 4
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.
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)
}
}
{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)
}
}
}
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{
{"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},
{"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)
}
}
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 {
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,
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 {
}
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 {
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,
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 {
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
}
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 {
}
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()}
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".
if err != nil {
return nil, err
}
- return a.(*IPAddr), nil
+ return a.toAddr().(*IPAddr), nil
}
// 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
"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:
}
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
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}
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}
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
}
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
}
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
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
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
}
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 {
"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
}
// 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
"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
// 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},
}
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
// 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) {
--- /dev/null
+// 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)
+ }
+ }
+}
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.
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
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 {
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)
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
}
}
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
}
// 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
"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
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
+ }
}
}
}
}
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
}
}
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)
}
ch := make(chan result)
go func() {
+ acquireThread()
+ defer releaseThread()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
addrs, err := gethostbyname(name)
}
func newLookupIP(name string) (addrs []IP, err error) {
+ acquireThread()
+ defer releaseThread()
hints := syscall.AddrinfoW{
Family: syscall.AF_UNSPEC,
Socktype: syscall.SOCK_STREAM,
}
func getservbyname(network, service string) (port int, err error) {
+ acquireThread()
+ defer releaseThread()
switch network {
case "tcp4", "tcp6":
network = "tcp"
}
ch := make(chan result)
go func() {
+ acquireThread()
+ defer releaseThread()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
port, err := getservbyname(network, service)
}
func newLookupPort(network, service string) (port int, err error) {
+ acquireThread()
+ defer releaseThread()
var stype int32
switch network {
case "tcp4", "tcp6":
}
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 {
}
func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
+ acquireThread()
+ defer releaseThread()
var target string
if service == "" && proto == "" {
target = name
}
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 {
}
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 {
}
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 {
}
func lookupAddr(addr string) (name []string, err error) {
+ acquireThread()
+ defer releaseThread()
arpa, err := reverseaddr(addr)
if err != nil {
return nil, err
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.
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 == '"' {
},
},
},
+ // 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 {
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package 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
+}
--- /dev/null
+// 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
+}
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" {
"errors"
"io"
"os"
- "sync"
"syscall"
"time"
)
if !c.ok() {
return syscall.EINVAL
}
- return setDeadline(c.fd, t)
+ return c.fd.setDeadline(t)
}
// SetReadDeadline implements the Conn SetReadDeadline method.
if !c.ok() {
return syscall.EINVAL
}
- return setReadDeadline(c.fd, t)
+ return c.fd.setReadDeadline(t)
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
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
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.
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
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
}
}
go func() {
+ defer ln.Close()
c, err := ln.Accept()
if err != nil {
t.Fatalf("Accept: %v", err)
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()
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)
+ }
+}
}
}
+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
}
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
}
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
}
}
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] {
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
// 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
}
defer c.Close()
c.SetKeepAlive(false)
+ c.SetKeepAlivePeriod(3 * time.Second)
c.SetLinger(0)
c.SetNoDelay(false)
c.LocalAddr()
} 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")
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)
}
} 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) {
} 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)
}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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) {
+}
// 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
}
client.mutex.Unlock()
client.sending.Unlock()
- if err != io.EOF && !closing {
+ if debugLog && err != io.EOF && !closing {
log.Println("rpc: client protocol error:", err)
}
}
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")
+ }
}
}
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
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.
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)
}
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 {
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()
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 {
// 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)
}
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
func startServer() {
Register(new(Arith))
+ RegisterName("net.rpc.Arith", new(Arith))
var l net.Listener
l, serverAddr = listenTCP()
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)
testRPC(t, serverAddr)
newOnce.Do(startNewServer)
testRPC(t, newServerAddr)
+ testNewServerRPC(t, newServerAddr)
}
func testRPC(t *testing.T, addr string) {
if err != nil {
t.Fatal("dialing", err)
}
+ defer client.Close()
// Synchronous calls
args := &Args{7, 8}
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) {
if err != nil {
t.Fatal("dialing", err)
}
+ defer client.Close()
// Synchronous calls
args := &Args{7, 8}
func testServeRequest(t *testing.T, server *Server) {
client := CodecEmulator{server: server}
+ defer client.Close()
args := &Args{7, 8}
reply := new(Reply)
func TestSendDeadlock(t *testing.T) {
client := NewClientWithCodec(WriteFailCodec(0))
+ defer client.Close()
done := make(chan bool)
go func() {
if err != nil {
t.Fatal("error dialing", err)
}
+ defer client.Close()
+
args := &Args{7, 8}
reply := new(Reply)
return testing.AllocsPerRun(100, func() {
}
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")
}
}
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")
}
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 {
if err != nil {
b.Fatal("error dialing:", err)
}
+ defer client.Close()
// Synchronous calls
args := &Args{7, 8}
if err != nil {
b.Fatal("error dialing:", err)
}
+ defer client.Close()
// Asynchronous calls
args := &Args{7, 8}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package 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
+}
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())
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())
"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.
//
// 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)
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
}
--- /dev/null
+// 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
+}
}
// 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)
}
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 {
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, "*")
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 {
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")
}
if err != nil {
t.Fatalf("NewClient: %v", err)
}
+ defer c.Close()
if ok, _ := c.Extension("DSN"); ok {
t.Fatalf("Shouldn't support DSN")
}
if err != nil {
t.Fatalf("NewClient: %v", err)
}
+ defer c.Close()
c.localName = "customhost"
err = nil
.
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
+`
// 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
--- /dev/null
+// 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
+}
// 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
}
+++ /dev/null
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build 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
-}
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()
// 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
"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.
// 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
}
// 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 (
"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))
}
// 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
}
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()
}
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()
}
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 {
l.Onoff = 0
l.Linger = 0
}
- if err := fd.incref(false); err != nil {
+ if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
// 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)
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))
}
// 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
}
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))))
}
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)))
}
// 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
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 {
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 {
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))
}
}
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)))
}
// 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
import (
"fmt"
+ "io"
"reflect"
"runtime"
+ "sync"
"testing"
"time"
)
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
}
}
+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{
{"", "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")},
}
{"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)
+ }
}
}
}
<-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
+ }
+}
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"
if err != nil {
return nil, err
}
- return a.(*TCPAddr), nil
+ return a.toAddr().(*TCPAddr), nil
}
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
}
// 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
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
// 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
}
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
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
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
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
}
}
// 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
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
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
}
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 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))
+}
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 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))
+}
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build 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)))
+}
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build 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))
+}
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 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
+}
// 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
//
// ...
// 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).
// }
//
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 {
}
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
}
}
+// 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;
// if _, _, err = c.ReadCodeLine(110); err != nil {
// return nil, err
// }
-// text, err := c.ReadDotAll()
+// text, err := c.ReadDotBytes()
// if err != nil {
// return nil, err
// }
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
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) {
}
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)
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
+}
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)
+ }
}
}
}
{"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},
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"
if err != nil {
return nil, err
}
- return a.(*UDPAddr), nil
+ return a.toAddr().(*UDPAddr), nil
}
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()
// 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
}
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
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}
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}
// 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
}
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
}
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
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)
}
}
defer func() {
- if recover() != nil {
- t.Fatalf("panicked")
+ if p := recover(); p != nil {
+ t.Fatalf("Listen, ListenPacket or protocol-specific Listen panicked: %v", p)
}
}()
}
}
-func TestUnixAutobind(t *testing.T) {
+func TestUnixgramAutobind(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("skipping: autobind is linux only")
}
}
}
+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 {
func TestUnixgramConnLocalAndRemoteNames(t *testing.T) {
for _, laddr := range []string{"", testUnixAddr()} {
+ laddr := laddr
taddr := testUnixAddr()
ta, err := ResolveUnixAddr("unixgram", taddr)
if err != nil {
var la *UnixAddr
if laddr != "" {
- var err error
if la, err = ResolveUnixAddr("unixgram", laddr); err != nil {
t.Fatalf("ResolveUnixAddr failed: %v", err)
}
}
func (a *UnixAddr) toAddr() Addr {
- if a == nil { // nil *UnixAddr
- return nil // nil interface
+ if a == nil {
+ return nil
}
return a
}
}
// 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
}
// 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
"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":
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)
}
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 {
}
}
+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 {
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
}
// 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
}
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
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
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
}
} 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 != "" {
},
"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
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 {
// 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
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()
}
// 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)
}
// 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)
}
// 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
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)
}
// 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
"io"
"os"
"strconv"
+ "sync"
"syscall"
)
// 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")
}
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")
// 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")
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)
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
// 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
// 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"
}
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)
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
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":
// 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{"/", "#", "./", "../"}
// 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
// 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 ../)
// 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
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 {
// 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 == `` {
// 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
// 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
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)
// Export for testing.
var Atime = atime
+var LstatP = &lstat
// 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
// 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}
}
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
// 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()
}
// 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
// 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
// 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)
// 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}
}
// 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
// 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}
}
// 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}
}
// 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}
}
// 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
// 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()
}
// 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 {
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
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),
// 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 ".".
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import "syscall"
+
+func init() {
+ useSyscallwd = useSyscallwdDarwin
+}
+
+func useSyscallwdDarwin(err error) bool {
+ return err != syscall.ENOTSUP
+}
"io"
"io/ioutil"
. "os"
+ osexec "os/exec"
"path/filepath"
"runtime"
"strings"
"syscall"
"testing"
+ "text/template"
"time"
)
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))
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 {
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())
}
}
}
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)
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)
+ }
+ })
+}
// 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
}
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
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)
+ }
+}
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)
}
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)
}
}
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)
}
}
// 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
// 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
// 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
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)
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)
// 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
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package 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)
+}
// 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.
--- /dev/null
+// 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
+}
// 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
// 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
"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 {
if e != nil {
return nil, e
}
+ defer t.Close()
u, e := t.GetTokenUser()
if e != nil {
return nil, e
// 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
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) {
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)
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)
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 {
{"[-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},
}
}
}
+ if testing.Short() {
+ t.Skip("skipping malloc count in short mode")
+ }
if runtime.GOMAXPROCS(0) > 1 {
t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
return
}
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)
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)
}
}
// 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
{"[-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},
}
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
}
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)"},
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 {
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
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)
}
}
/* 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")
}
}
}
+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
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) {
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)
}
}
+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:
//
// 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()
}
}
// 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 {
}
// ... 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() {
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
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
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)
}
// 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
+}
// 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.
//
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.
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.
_GC_ARRAY_START
_GC_ARRAY_NEXT
_GC_CALL
- _GC_MAP_PTR
_GC_CHAN_PTR
_GC_STRING
_GC_EFACE
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
mt.key = ktyp
mt.elem = etyp
+ // mt.bucket = bucketOf(ktyp, etyp)
+ // mt.hmap = hMapOf(mt.bucket)
mt.uncommonType = nil
mt.ptrToThis = nil
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 {
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))
// 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)
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.
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.
*(*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 {
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")
}
// 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}
}
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)
}
}
}
--- /dev/null
+// 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")
+}
"compress/bzip2"
"fmt"
"io"
- "math/rand"
"os"
"path/filepath"
"regexp/syntax"
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 {
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
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) }
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
}
^ 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:
\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])
// 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.
// 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)
// 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
}
}
}
}
+
+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)
+ }
+}
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)
}
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) }
package runtime_test
import (
+ "runtime"
"testing"
)
}
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 = `
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")
+}
+`
"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 {
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)
}
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))
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)
}
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) {
}
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) {
}
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 = `
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
+ }
+}
+`
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;
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)
+}
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) {
*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
}
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
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
// 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
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.
// 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
// 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
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
--- /dev/null
+// 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)
+}
}
}
}
+
+// 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")
+ }
+}
}
}
+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++ {
_ = 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]
+ }
+}
// 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
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)
--- /dev/null
+// 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) }
"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) {
}
--- /dev/null
+// 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
+ }
+}
// 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
"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.
}
fmt.Fprint(w, "\n")
if debug > 0 {
- printStackRecord(w, r.Stack(), false)
+ printStackRecord(w, r.Stack(), true)
}
}
"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)
}
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()
}
"math"
"runtime"
"sync/atomic"
+ "syscall"
"testing"
"time"
)
<-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)
}
}
-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++ {
}
}
+func BenchmarkStackGrowth(b *testing.B) {
+ benchmarkStackGrowth(b, 10)
+}
+
+func BenchmarkStackGrowthDeep(b *testing.B) {
+ benchmarkStackGrowth(b, 1024)
+}
+
func BenchmarkCreateGoroutines(b *testing.B) {
benchmarkCreateGoroutines(b, 1)
}
import (
"io"
+ // "io/ioutil"
+ // "os"
+ // "os/exec"
+ // . "runtime"
+ // "strconv"
+ // "strings"
"testing"
)
}(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")
+}
+*/
"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]
}
// 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)
}
// Call its Sort method to sort the data.
func OrderedBy(less ...lessFunc) *multiSorter {
return &multiSorter{
- changes: changes,
- less: less,
+ less: less,
}
}
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)
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}]
}
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sort_test
+
+import (
+ "fmt"
+ "sort"
+)
+
+type Grams int
+
+func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }
+
+type Organ struct {
+ Name string
+ Weight Grams
+}
+
+type Organs []*Organ
+
+func (s Organs) Len() int { return len(s) }
+func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// ByName implements sort.Interface by providing Less and using the Len and
+// Swap methods of the embedded Organs value.
+type ByName struct{ Organs }
+
+func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }
+
+// ByWeight implements sort.Interface by providing Less and using the Len and
+// Swap methods of the embedded Organs value.
+type ByWeight struct{ Organs }
+
+func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }
+
+func 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)
+ }
+}
}
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")
}
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)
// 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))
+
+*/
}
}
+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++ {
}
}
+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++ {
}
}
+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
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}
}
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)
}
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.
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") }
// 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)
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 {
//
// 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"
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)...)
)
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")
}
--- /dev/null
+/* 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;
+}
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
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)
}
--- /dev/null
+// 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
}
}
+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
}
}
+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>",
}
return
}
+
+func swapUint64(addr *uint64, new uint64) (old uint64) {
+ for {
+ old = *addr
+ if CompareAndSwapUint64(addr, old, new) {
+ break
+ }
+ }
+ return
+}
#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)
}
_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)
}
_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)
}
_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)
}
_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)
}
_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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
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)
}
void *LoadPointer (void **addr)
- __asm__ (GOSYM_PREFIX "sync_atomic.LoadPointer");
+ __asm__ (GOSYM_PREFIX "sync_atomic.LoadPointer")
+ __attribute__ ((no_split_stack));
void *
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)
}
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)
}
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)
}
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)
}
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)
}
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)
package atomic_test
import (
+ "fmt"
"runtime"
+ "strings"
. "sync/atomic"
"testing"
"unsafe"
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
}
}
-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)
}
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 {
}
}
+func TestCompareAndSwapUint64(t *testing.T) {
+ testCompareAndSwapUint64(t, CompareAndSwapUint64)
+}
+
func TestCompareAndSwapUintptr(t *testing.T) {
var x struct {
before uintptr
// 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))
+ }
}
}
}
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))
+ }
}
}
}
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)
}
}
}
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()
+ }()
+ }
+}
// 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:
//
// 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)
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.
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))
}
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.
// 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.
// 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()
}
// 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.
// 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")
}
}
import (
. "sync"
+
+ "runtime"
"testing"
)
}
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
+ }
+}
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() {
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
func raceEnable() {
runtime.RaceEnable()
}
+
+func raceRead(addr unsafe.Pointer) {
+ runtime.RaceRead(addr)
+}
+
+func raceWrite(addr unsafe.Pointer) {
+ runtime.RaceWrite(addr)
+}
func raceEnable() {
}
+
+func raceRead(addr unsafe.Pointer) {
+}
+
+func raceWrite(addr unsafe.Pointer) {
+}
package sync
+import "unsafe"
+
// defined in package runtime
// Semacquire waits until *s > 0 and then atomically decrements it.
// 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))
+}
// 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")
}
// 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 {
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)
}
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)
}
// 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
--- /dev/null
+// 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
+ )
+}
// 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.
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 {
// 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
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) {
// 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
}
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) {
// 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
}
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.
// 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.
// 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
"net"
"os"
"os/exec"
+ "runtime"
"syscall"
"testing"
"time"
// "-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
func raceReleaseMerge(addr unsafe.Pointer) {
}
+
+func raceReadRange(addr unsafe.Pointer, len int) {
+}
+
+func raceWriteRange(addr unsafe.Pointer, len int) {
+}
--- /dev/null
+// 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)
+ }
+}
// 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
// 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
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
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 {
--- /dev/null
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 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
+}
return UTF16ToString(b), nil
}
+const (
+ // do not reorder
+ NetSetupUnknownStatus = iota
+ NetSetupUnjoined
+ NetSetupWorkgroupName
+ NetSetupDomainName
+)
+
type UserInfo10 struct {
Name *uint16
Comment *uint16
}
//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 (
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;
// 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
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,
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
--- /dev/null
+// 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", "")
+}
// 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
//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
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
}
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
)
// 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
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))
}
func roundDown10(n int) int {
var tens = 0
// tens = floor(log_10(n))
- for n > 10 {
+ for n >= 10 {
n = n / 10
tens++
}
// 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.
--- /dev/null
+// 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)
+ }
+ }
+}
--- /dev/null
+// 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)
+}
--- /dev/null
+// 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
+)
// 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
}
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.
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
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)
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
// 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) {
// 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")
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 {
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()
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
}
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
t.signal <- t
}()
+ t.start = time.Now()
test.F(t)
}
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()
}
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
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.
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
// 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))
+ })
}
}
}
}
-// 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))
}
}
*/
// {{/* 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}}
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;
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
}
}
-// 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() {
// 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) {
} 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.
U16 uint16
X string
FloatZero float64
- ComplexZero float64
+ ComplexZero complex128
// Nested structs.
U *U
// Struct with String method.
Err error
// Pointers
PI *int
+ PS *string
PSI *[]int
NIL *int
// Function (not method)
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.
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
}
// 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 {
// 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},
{"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},
{"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},
"<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},
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())
+ }
+ }
+}
import (
"bytes"
+ "errors"
"fmt"
"io"
"net/url"
"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)
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
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 (
// 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.
// 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
}
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,
// 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
}
{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.
"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.
}
func (t *TextNode) String() string {
- return fmt.Sprintf("%q", t.Text)
+ return fmt.Sprintf(textFormat, t.Text)
}
func (t *TextNode) Copy() Node {
"runtime"
"strconv"
"strings"
- "unicode"
)
// Tree is the representation of a single parsed template.
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
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
}
}
-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)
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)
// {{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:
// {{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:
// {{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:
// {{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())
}
`{{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,
{"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, ""},
}
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 {
{"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},
}
}
}
+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.
ResetLocalOnceForTest()
localOnce.Do(initTestingZone)
}
+
+var ParseTimeZone = parseTimeZone
)
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
// 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) {
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
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:]
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:]
}
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]
// 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)
} 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, '+')
}
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...)
// 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
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
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 '-':
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
}
// 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
}
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
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 {
--- /dev/null
+// 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}}}
+
+`
package time
+import (
+ "errors"
+ "runtime"
+)
+
func init() {
// force US/Pacific for time zone tests
ForceUSPacificForTesting()
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()
+ }
+ }
+}
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 {
"fmt"
"runtime"
"sort"
+ "sync"
"sync/atomic"
"testing"
. "time"
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) {
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())
+ }
+}
// 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
// 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
// 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.
//
// 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.
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,
// 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.
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 {
} 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),
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:]
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
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]")
}
// 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].
sec--
}
}
- return Time{sec + unixToInternal, int32(nsec), Local}
+ return Time{sec + unixToInternal, uintptr(nsec), Local}
}
func isLeap(year int) bool {
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).
// 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
}
}
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,
if u0 < u0x {
u1++
}
- u0x, u0 = u0, u0+uint64(t.nsec)
+ u0x, u0 = u0, u0+uint64(nsec)
if u0 < u0x {
u1++
}
{"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"},
// 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
}
}
+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
}
}
+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
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) {
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)
+ }
}
}
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) {
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)
+ }
}
}
{"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},
}
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")
}
}
}
+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()
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.
--- /dev/null
+// 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
+}
import "errors"
-const (
- headerSize = 4 + 16 + 4*7
-)
-
// Simple I/O interface to binary blob of data.
type data struct {
p []byte
// 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.
"errors"
"runtime"
"syscall"
+ "unsafe"
)
// TODO(rsc): Fall back to copy of zoneinfo files.
// 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))
}
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.
}
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
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
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 {
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
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
}
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
}
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)
}
#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
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"
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
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 $?
#define NOSELGEN 1
-static int32 debug = 0;
-
typedef struct WaitQ WaitQ;
typedef struct SudoG SudoG;
typedef struct Select Select;
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
enum
{
+ debug = 0,
+
// Scase.kind
CaseRecv,
CaseSend,
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;
}
return; // not reached
}
- if(runtime_gcwaiting)
+ if(runtime_gcwaiting())
runtime_gosched();
if(debug) {
}
runtime_lock(c);
- // TODO(dvyukov): add similar instrumentation to select.
if(raceenabled)
runtime_racereadpc(c, pc, runtime_chansend);
if(c->closed)
int64 t0;
G *g;
- if(runtime_gcwaiting)
+ if(runtime_gcwaiting())
runtime_gosched();
if(debug)
{
Select *sel;
uint32 o, i, j, k;
+ int64 t0;
Scase *cas, *dfl;
Hchan *c;
SudoG *sg;
G *g;
sel = *selp;
- if(runtime_gcwaiting)
+ if(runtime_gcwaiting())
runtime_gosched();
if(debug)
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
break;
case CaseSend:
+ if(raceenabled)
+ runtime_racereadpc(c, runtime_selectgo, runtime_chansend);
if(c->closed)
goto sclose;
if(c->dataqsiz > 0) {
if(sg != nil) {
gp = sg->g;
selunlock(sel);
+ if(sg->releasetime)
+ sg->releasetime = runtime_cputicks();
runtime_ready(gp);
} else {
selunlock(sel);
if(sg != nil) {
gp = sg->g;
selunlock(sel);
+ if(sg->releasetime)
+ sg->releasetime = runtime_cputicks();
runtime_ready(gp);
} else {
selunlock(sel);
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;
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;
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);
}
if(raceenabled) {
- runtime_racewritepc(c, runtime_getcallerpc(&c), runtime_closechan);
+ runtime_racewritepc(c, pc, runtime_closechan);
runtime_racerelease(c);
}
break;
gp = sg->g;
gp->param = nil;
+ if(sg->releasetime)
+ sg->releasetime = runtime_cputicks();
runtime_ready(gp);
}
break;
gp = sg->g;
gp->param = nil;
+ if(sg->releasetime)
+ sg->releasetime = runtime_cputicks();
runtime_ready(gp);
}
}
// 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");
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);
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) {
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;
// 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"
const byte*
runtime_getenv(const char *s)
{
- int32 i, j, len;
+ int32 i, j;
+ intgo len;
const byte *v, *bs;
String* envv;
int32 envc;
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;
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;
+}
slen += 4;
}
- retdata = runtime_mallocgc ((uintptr) slen, FlagNoPointers, 1, 0);
+ retdata = runtime_mallocgc ((uintptr) slen, 0, FlagNoScan);
ret.str = retdata;
ret.len = slen;
}
}
- retdata = runtime_mallocgc (len, FlagNoPointers, 1, 0);
+ retdata = runtime_mallocgc (len, 0, FlagNoScan);
__builtin_memcpy (retdata, buf, len);
ret.str = retdata;
ret.len = 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;
}
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);
}
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;
#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. */
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;
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)
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;
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;
}
}
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);
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;
}
}
// 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"
{
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.
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);
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;
}
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) {
}
}
}
+
+ if(--runtime_m()->locks < 0)
+ runtime_throw("runtime_unlock: lock count");
}
// One-time notifications.
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)
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
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;
+}
#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;
// 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();
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.
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)
}
}
- 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();
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.
// 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];
// 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;
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) {
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;
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.
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
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();
// 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
// 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
// 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
// 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.
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);
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;
// 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;
}
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);
}
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;
// (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;
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;
} 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.
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;
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)
{
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) {
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
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");
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");
}
// 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
// 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
//
// 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.
// 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);
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.
// 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);
{
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);
struct MTypes
{
byte compression; // one of MTypes_*
- bool sysalloc; // whether (void*)data is from runtime_SysAlloc
uintptr data;
};
};
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.
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;
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);
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);
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;
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,
// 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.
#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;
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;
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
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;
+ }
}
}
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.
}
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);
}
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");
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);
}
}
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);
}
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);
}
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);
}
return nil;
}
+ runtime_xadd64(stat, n);
return p;
}
}
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);
}
// 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;
}
}
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) {
#include "runtime.h"
#include "arch.h"
#include "malloc.h"
+#include "go-type.h"
enum { debug = 0 };
{
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.
} 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;
t->fkey[i] = k;
t->val[i].fn = fn;
t->val[i].ft = ft;
+ t->val[i].ot = ot;
}
static bool
t->fkey[i] = (void*)-1;
t->val[i].fn = nil;
t->val[i].ft = nil;
+ t->val[i].ot = nil;
t->ndead++;
}
return true;
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);
}
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;
resizefintab(tab);
}
- addfintab(tab, p, f, ft);
+ addfintab(tab, p, f, ft, ot);
runtime_setblockspecial(p, true);
runtime_unlock(tab);
return true;
// 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;
return false;
*fn = f.fn;
*ft = f.ft;
+ *ot = f.ot;
return true;
}
// 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*
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;
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.
// 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
// /* 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 */
//
uint32 runtime_worldsema = 1;
-static int32 gctrace;
-
// The size of Workbuf is N*PageSize.
typedef struct Workbuf Workbuf;
struct Workbuf
FuncVal *fn;
void *arg;
const struct __go_func_type *ft;
+ const struct __go_ptr_type *ot;
};
typedef struct FinBlock FinBlock;
enum {
GC_DEFAULT_PTR = GC_NUM_INSTR,
- GC_MAP_NEXT,
GC_CHAN,
GC_NUM_INSTR2
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
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.
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
Workbuf *wbuf;
PtrTarget *ptrbuf_end;
- arena_start = runtime_mheap->arena_start;
+ arena_start = runtime_mheap.arena_start;
wp = *_wp;
wbuf = *_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");
}
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;
obj = (byte*)obj - (shift-j)*PtrSize;
shift = j;
bits = xbits>>shift;
+ if(CollectStats)
+ runtime_xadd64(&gcstats.flushptrbuf.foundword, 1);
goto found;
}
}
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;
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
}
// 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.
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);
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
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));
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;
}
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,
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");
}
}
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;
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
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;
// (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;
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;
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];
#if 0
case GC_CHAN_PTR:
- // Similar to GC_MAP_PTR
chan = *(Hchan**)(stack_top.b + pc[1]);
if(chan == nil) {
pc += 3;
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;
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;
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);
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");
}
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;
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:
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:
{
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;
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;
finq->cnt++;
f->fn = fn;
f->ft = ft;
+ f->ot = ot;
f->arg = p;
runtime_unlock(&finlock);
return true;
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;
// 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) {
}
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);
}
}
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;
{
uint32 spanidx;
- for(spanidx=0; spanidx<runtime_mheap->nspan; spanidx++) {
+ for(spanidx=0; spanidx<runtime_mheap.nspan; spanidx++) {
dumpspan(spanidx);
}
}
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)
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)
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
// 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
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;
}
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);
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;
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,
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 *)
// 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*)
// 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);
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;
}
{
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;
}
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
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;
}
// 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 {
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 {
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;
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;
// 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);
}
{
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");
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;
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(;;) {
bits = obits | (bitSpecial<<shift);
else
bits = obits & ~(bitSpecial<<shift);
- if(runtime_singleproc) {
+ if(runtime_gomaxprocs == 1) {
*b = bits;
break;
} else {
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;
}
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)
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;
// 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]);
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*
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;
// 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;
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);
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;
}
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;
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.
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;
}
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;
MSpan *t;
PageID p;
- if(s->types.sysalloc)
- runtime_settype_sysfree(s);
s->types.compression = MTypes_Empty;
if(s->state != MSpanInUse || s->ref != 0) {
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.
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;
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.
MHeap *h;
uint64 tick, now, forcegc, limit;
uint32 k;
- uintptr sumreleased;
- const byte *env;
- bool trace;
Note note, *notep;
USED(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();
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);
- }
}
}
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.
#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.
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.
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;
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;
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];
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);
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) {
b->recent_free_bytes += size;
}
runtime_unlock(&proflock);
- m = runtime_m();
- m->nomemprof--;
}
int64 runtime_blockprofilerate; // in CPU ticks
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
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;
USED(sp);
runtime_goroutineheader(g);
runtime_traceback();
- runtime_goroutinetrailer(g);
+ runtime_printcreatedby(g);
if(all)
runtime_tracebackothers(g);
n = b.__count - g->writenbuf;
ok = false;
n = runtime_gcount();
if(n <= b.__count) {
- runtime_semacquire(&runtime_worldsema);
+ runtime_semacquire(&runtime_worldsema, false);
runtime_m()->gcing = 1;
runtime_stoptheworld();
void
runtime_mprofinit(void)
{
- addrhash = allocate((1<<AddrHashBits)*sizeof *addrhash);
+ addrhash = runtime_persistentalloc((1<<AddrHashBits)*sizeof *addrhash, 0, &mstats.buckhash_sys);
}
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
// 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
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");
// 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:
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");
// 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
// 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);
{
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)
// 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);
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)
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) {
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')
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) {
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;
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)
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;
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;
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;
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;
}
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)
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];
}
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;
}
// 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).
}
int32
-runtime_netpollopen(int32 fd, PollDesc *pd)
+runtime_netpollopen(uintptr fd, PollDesc *pd)
{
Kevent ev[2];
int32 n;
// 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.
static int32 lasterr;
Kevent events[64], *ev;
Timespec ts, *tp;
- int32 n, i;
+ int32 n, i, mode;
G *gp;
if(kq == -1)
}
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;
// 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"
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
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
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;
{
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);
}
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) {
}
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;
}
#include <stdarg.h>
#include "runtime.h"
#include "array.h"
+#include "go-type.h"
//static Lock debuglock;
// 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();
void
runtime_printstring(String v)
{
- // extern uint32 runtime_maxstring;
-
// if(v.len > runtime_maxstring) {
// gwrite("[string too long]", 17);
// return;
}
// 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
// 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;
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;
Lock gflock;
G* gfree;
+ uint32 gcwaiting; // gc is waiting to run
int32 stopwait;
Note stopnote;
uint32 sysmonwait;
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;
M* runtime_extram;
int8* runtime_goos;
int32 runtime_ncpu;
+bool runtime_precisestack;
static int32 newprocs;
void* runtime_mstart(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:
//
{
int32 n, procs;
const byte *p;
+ Eface i;
m = &runtime_m0;
g = &runtime_g0;
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;
// 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,
// 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
}
void
-runtime_goroutinetrailer(G *g)
+runtime_printcreatedby(G *g)
{
if(g != nil && g->gopc != 0 && g->goid != 1) {
String fn;
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;
// 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");
}
}
runtime_lock(&runtime_sched);
mp->id = runtime_sched.mcount++;
-
+ checkmcount();
runtime_mpreinit(mp);
// Add to runtime_allm so garbage collector doesn't free m
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");
runqput(m->p, gp);
if(runtime_atomicload(&runtime_sched.npidle) != 0 && runtime_atomicload(&runtime_sched.nmspinning) == 0) // TODO: fast atomic
wakep();
+ m->locks--;
}
int32
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)
{
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--;
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);
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();
newprocs = 0;
} else
procresize(runtime_gomaxprocs);
- runtime_gcwaiting = 0;
+ runtime_sched.gcwaiting = 0;
p1 = nil;
while((p = pidleget()) != nil) {
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;
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) {
// the maximum number of procs.
newm(mhelpgc, nil);
}
+ m->locks--;
}
// Called to start an M.
// 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();
{
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
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)
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);
p = releasep();
handoffp(p);
}
- inclocked(1);
+ incidlelocked(1);
// Wait until another thread schedules lockedg again.
runtime_notesleep(&m->park);
runtime_noteclear(&m->park);
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);
{
P *p;
- if(!runtime_gcwaiting)
+ if(!runtime_sched.gcwaiting)
runtime_throw("gcstopm: not waiting for gc");
if(m->spinning) {
m->spinning = false;
runtime_throw("execute: bad g status");
}
gp->status = Grunning;
- m->p->tick++;
+ m->p->schedtick++;
m->curg = gp;
gp->m = m;
int32 i;
top:
- if(runtime_gcwaiting) {
+ if(runtime_sched.gcwaiting) {
gcstopm();
goto 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;
}
// 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)
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;
}
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
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;
}
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;
}
// 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)
{
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;
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,
}
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)
}
runtime_unlock(&runtime_sched);
}
+
+ m->locks--;
}
// The same as runtime_entersyscall(), but with a hint that the syscall is blocking.
{
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,
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.
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.
#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);
#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.
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);
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)
/* 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()
{
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) {
}
runtime_unlock(&runtime_sched);
- runtime_semacquire(&runtime_worldsema);
+ runtime_semacquire(&runtime_worldsema, false);
m->gcing = 1;
runtime_stoptheworld();
newprocs = 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;
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;
runtime_UnlockOSThread(void)
{
m->locked &= ~LockExternal;
- UnlockOSThread();
+ unlockOSThread();
}
void
if(m->locked < LockInternal)
runtime_throw("runtime: internal error: misuse of lockOSThread/unlockOSThread");
m->locked -= LockInternal;
- UnlockOSThread();
+ unlockOSThread();
}
bool
return runtime_lockedOSThread();
}
-// for testing of wire, unwire
-uint32
-runtime_mid()
-{
- return m->id;
-}
-
intgo runtime_NumGoroutine (void)
__asm__ (GOSYM_PREFIX "runtime.NumGoroutine");
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);
}
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);
if(hz != 0)
runtime_resetcpuprofiler(hz);
+
+ m->locks--;
}
// Change number of processors. The world is stopped, sched is locked.
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);
}
}
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);
}
}
p->status = Pidle;
pidleput(p);
}
- runtime_singleproc = new == 1;
runtime_atomicstore((uint32*)&runtime_gomaxprocs, new);
}
}
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);
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;
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(;;) {
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);
// 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
// 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;
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;
}
}
+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))
{
{
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;
+}
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);
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");
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;
+}
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;
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;
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;
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;
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
{
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.
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,
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
// 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
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
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
#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();
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.
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);
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);
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);
* 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
__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);
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
void __go_set_closure(void*);
void* __go_get_closure(void);
+
+bool runtime_gcwaiting(void);
+void runtime_badsignal(int);
#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;
};
}
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;
}
static void
-semdequeue(SemaRoot *root, Sema *s)
+semdequeue(SemaRoot *root, SemaWaiter *s)
{
if(s->next)
s->next->prev = s->prev;
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;
}
void
-runtime_semacquire(uint32 volatile *addr)
-{
- semacquireimpl(addr, 0);
-}
-
-void
runtime_semrelease(uint32 volatile *addr)
{
- Sema *s;
+ SemaWaiter *s;
SemaRoot *root;
root = semroot(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);
+}
// 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>
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;
}
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;
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);
+}
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)
{
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;
return s;
}
+String runtime_cstringToGo(byte*)
+ __asm__ (GOSYM_PREFIX "runtime.cstringToGo");
+
+String
+runtime_cstringToGo(byte *str)
+{
+ return runtime_gostringnocopy(str);
+}
+
enum
{
Runeself = 0x80,
#include "runtime.h"
#include "defs.h"
+#include "signal_unix.h"
// Linux futex.
//
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.
#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.
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;
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.
{
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
siftup(i);
siftdown(i);
}
+ if(debug)
+ dumptimers("deltimer");
runtime_unlock(&timers);
return true;
}
for(;;) {
runtime_lock(&timers);
+ timers.sleeping = false;
now = runtime_nanotime();
for(;;) {
if(timers.len == 0) {
timers.sleeping = true;
runtime_noteclear(&timers.waitnote);
runtime_unlock(&timers);
- runtime_entersyscallblock();
- runtime_notetsleep(&timers.waitnote, delta);
- runtime_exitsyscall();
+ runtime_notetsleepg(&timers.waitnote, delta);
}
}
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;
}
}
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))
{