var t testing.T
// make sure error mentions that
- // ch is unexported, not just "ch not found".
+ // name is unexported, not just "name not found".
- t.ch = nil // ERROR "unexported"
+ t.name = nil // ERROR "unexported"
println(testing.anyLowercaseName("asdf")) // ERROR "unexported" "undefined: testing.anyLowercaseName"
}
-82fdc445f2ff
+4a8268927758
The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources.
image/gif.gox \
image/jpeg.gox \
image/png.gox \
- image/tiff.gox \
- image/ycbcr.gox
+ image/tiff.gox
toolexeclibgoindexdir = $(toolexeclibgodir)/index
go/image/format.go \
go/image/geom.go \
go/image/image.go \
- go/image/names.go
+ go/image/names.go \
+ go/image/ycbcr.go
go_io_files = \
go/io/multi.go \
go_net_fd_os_file = go/net/fd_linux.go
go_net_newpollserver_file = go/net/newpollserver.go
else # !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
+if LIBGO_IS_NETBSD
+go_net_fd_os_file = go/net/fd_netbsd.go
+go_net_newpollserver_file = go/net/newpollserver.go
+else # !LIBGO_IS_NETBSD && !LIBGO_IS_LINUX && !LIBGO_IS_RTEMS
# By default use select with pipes. Most systems should have
# something better.
go_net_fd_os_file = go/net/fd_select.go
go_net_newpollserver_file = go/net/newpollserver.go
+endif # !LIBGO_IS_NETBSD
endif # !LIBGO_IS_LINUX
endif # !LIBGO_IS_RTEMS
if LIBGO_IS_LINUX
go_net_interface_file = go/net/interface_linux.go
else
+if LIBGO_IS_NETBSD
+go_net_interface_file = go/net/interface_netbsd.go
+else
go_net_interface_file = go/net/interface_stub.go
endif
+endif
go_net_files = \
go/net/cgo_unix.go \
go/strconv/atof.go \
go/strconv/atoi.go \
go/strconv/decimal.go \
+ go/strconv/extfloat.go \
go/strconv/ftoa.go \
go/strconv/itoa.go \
go/strconv/quote.go
go_testing_files = \
go/testing/benchmark.go \
go/testing/example.go \
- go/testing/testing.go
+ go/testing/testing.go \
+ go/testing/wrapper.go
go_time_files = \
go/time/format.go \
go_go_doc_files = \
go/go/doc/comment.go \
go/go/doc/doc.go \
- go/go/doc/example.go
+ go/go/doc/example.go \
+ go/go/doc/exports.go \
+ go/go/doc/filter.go
go_go_parser_files = \
go/go/parser/interface.go \
go/go/parser/parser.go
go/image/bmp/reader.go
go_image_color_files = \
- go/image/color/color.go
+ go/image/color/color.go \
+ go/image/color/ycbcr.go
go_image_draw_files = \
go/image/draw/draw.go
go/image/tiff/consts.go \
go/image/tiff/reader.go
-go_image_ycbcr_files = \
- go/image/ycbcr/ycbcr.go
-
go_index_suffixarray_files = \
go/index/suffixarray/qsufsort.go \
go/index/suffixarray/suffixarray.go
go/net/http/filetransport.go \
go/net/http/fs.go \
go/net/http/header.go \
+ go/net/http/jar.go \
go/net/http/lex.go \
go/net/http/request.go \
go/net/http/response.go \
image/jpeg.lo \
image/png.lo \
image/tiff.lo \
- image/ycbcr.lo \
index/suffixarray.lo \
io/ioutil.lo \
log/syslog.lo \
@$(CHECK)
.PHONY: image/tiff/check
-@go_include@ image/ycbcr.lo.dep
-image/ycbcr.lo.dep: $(go_image_ycbcr_files)
- $(BUILDDEPS)
-image/ycbcr.lo: $(go_image_ycbcr_files)
- $(BUILDPACKAGE)
-image/ycbcr/check: $(CHECK_DEPS)
- @$(MKDIR_P) image/ycbcr
- @$(CHECK)
-.PHONY: image/ycbcr/check
-
@go_include@ index/suffixarray.lo.dep
index/suffixarray.lo.dep: $(go_index_suffixarray_files)
$(BUILDDEPS)
$(BUILDGOX)
image/tiff.gox: image/tiff.lo
$(BUILDGOX)
-image/ycbcr.gox: image/ycbcr.lo
- $(BUILDGOX)
index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX)
hash/crc32/check \
hash/crc64/check \
hash/fnv/check \
+ image/color/check \
image/draw/check \
image/jpeg/check \
image/png/check \
image/tiff/check \
- image/ycbcr/check \
index/suffixarray/check \
io/ioutil/check \
log/syslog/check \
hash/fnv.lo net/http/cgi.lo net/http/fcgi.lo \
net/http/httptest.lo net/http/httputil.lo net/http/pprof.lo \
image/bmp.lo image/color.lo image/draw.lo image/gif.lo \
- image/jpeg.lo image/png.lo image/tiff.lo image/ycbcr.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/mime.lo mime/multipart.lo net/dict.lo net/http.lo \
- net/mail.lo net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
- old/netchan.lo old/regexp.lo old/template.lo \
- $(am__DEPENDENCIES_1) os/user.lo os/signal.lo path/filepath.lo \
- regexp/syntax.lo net/rpc/jsonrpc.lo runtime/debug.lo \
- runtime/pprof.lo sync/atomic.lo sync/atomic_c.lo \
- syscall/syscall.lo syscall/errno.lo syscall/wait.lo \
- text/scanner.lo text/tabwriter.lo text/template.lo \
- text/template/parse.lo testing/testing.lo testing/iotest.lo \
- testing/quick.lo testing/script.lo unicode/utf16.lo \
- unicode/utf8.lo
+ image/jpeg.lo image/png.lo image/tiff.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/mime.lo mime/multipart.lo \
+ net/dict.lo net/http.lo net/mail.lo net/rpc.lo net/smtp.lo \
+ net/textproto.lo net/url.lo old/netchan.lo old/regexp.lo \
+ old/template.lo $(am__DEPENDENCIES_1) os/user.lo os/signal.lo \
+ path/filepath.lo regexp/syntax.lo net/rpc/jsonrpc.lo \
+ runtime/debug.lo runtime/pprof.lo sync/atomic.lo \
+ sync/atomic_c.lo syscall/syscall.lo syscall/errno.lo \
+ syscall/wait.lo text/scanner.lo text/tabwriter.lo \
+ text/template.lo text/template/parse.lo testing/testing.lo \
+ testing/iotest.lo testing/quick.lo testing/script.lo \
+ unicode/utf16.lo unicode/utf8.lo
libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
image/gif.gox \
image/jpeg.gox \
image/png.gox \
- image/tiff.gox \
- image/ycbcr.gox
+ image/tiff.gox
toolexeclibgoindexdir = $(toolexeclibgodir)/index
toolexeclibgoindex_DATA = \
go/image/format.go \
go/image/geom.go \
go/image/image.go \
- go/image/names.go
+ go/image/names.go \
+ go/image/ycbcr.go
go_io_files = \
go/io/multi.go \
# By default use select with pipes. Most systems should have
# something better.
-@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_select.go
+@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_netbsd.go
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_fd_os_file = go/net/fd_linux.go
@LIBGO_IS_RTEMS_TRUE@go_net_fd_os_file = go/net/fd_select.go
-@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
@LIBGO_IS_LINUX_TRUE@@LIBGO_IS_RTEMS_FALSE@go_net_newpollserver_file = go/net/newpollserver.go
@LIBGO_IS_RTEMS_TRUE@go_net_newpollserver_file = go/net/newpollserver_rtems.go
@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_cgo_file = go/net/cgo_bsd.go
@LIBGO_IS_LINUX_TRUE@go_net_sock_file = go/net/sock_linux.go
@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go
@LIBGO_IS_LINUX_TRUE@go_net_sendfile_file = go/net/sendfile_linux.go
-@LIBGO_IS_LINUX_FALSE@go_net_interface_file = go/net/interface_stub.go
+@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@go_net_interface_file = go/net/interface_stub.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
go_net_files = \
go/net/cgo_unix.go \
go/strconv/atof.go \
go/strconv/atoi.go \
go/strconv/decimal.go \
+ go/strconv/extfloat.go \
go/strconv/ftoa.go \
go/strconv/itoa.go \
go/strconv/quote.go
go_testing_files = \
go/testing/benchmark.go \
go/testing/example.go \
- go/testing/testing.go
+ go/testing/testing.go \
+ go/testing/wrapper.go
go_time_files = \
go/time/format.go \
go_go_doc_files = \
go/go/doc/comment.go \
go/go/doc/doc.go \
- go/go/doc/example.go
+ go/go/doc/example.go \
+ go/go/doc/exports.go \
+ go/go/doc/filter.go
go_go_parser_files = \
go/go/parser/interface.go \
go/image/bmp/reader.go
go_image_color_files = \
- go/image/color/color.go
+ go/image/color/color.go \
+ go/image/color/ycbcr.go
go_image_draw_files = \
go/image/draw/draw.go
go/image/tiff/consts.go \
go/image/tiff/reader.go
-go_image_ycbcr_files = \
- go/image/ycbcr/ycbcr.go
-
go_index_suffixarray_files = \
go/index/suffixarray/qsufsort.go \
go/index/suffixarray/suffixarray.go
go/net/http/filetransport.go \
go/net/http/fs.go \
go/net/http/header.go \
+ go/net/http/jar.go \
go/net/http/lex.go \
go/net/http/request.go \
go/net/http/response.go \
image/jpeg.lo \
image/png.lo \
image/tiff.lo \
- image/ycbcr.lo \
index/suffixarray.lo \
io/ioutil.lo \
log/syslog.lo \
hash/crc32/check \
hash/crc64/check \
hash/fnv/check \
+ image/color/check \
image/draw/check \
image/jpeg/check \
image/png/check \
image/tiff/check \
- image/ycbcr/check \
index/suffixarray/check \
io/ioutil/check \
log/syslog/check \
@$(CHECK)
.PHONY: image/tiff/check
-@go_include@ image/ycbcr.lo.dep
-image/ycbcr.lo.dep: $(go_image_ycbcr_files)
- $(BUILDDEPS)
-image/ycbcr.lo: $(go_image_ycbcr_files)
- $(BUILDPACKAGE)
-image/ycbcr/check: $(CHECK_DEPS)
- @$(MKDIR_P) image/ycbcr
- @$(CHECK)
-.PHONY: image/ycbcr/check
-
@go_include@ index/suffixarray.lo.dep
index/suffixarray.lo.dep: $(go_index_suffixarray_files)
$(BUILDDEPS)
$(BUILDGOX)
image/tiff.gox: image/tiff.lo
$(BUILDGOX)
-image/ycbcr.gox: image/ycbcr.lo
- $(BUILDGOX)
index/suffixarray.gox: index/suffixarray.lo
$(BUILDGOX)
LIBGO_IS_SOLARIS_TRUE
LIBGO_IS_RTEMS_FALSE
LIBGO_IS_RTEMS_TRUE
+LIBGO_IS_NETBSD_FALSE
+LIBGO_IS_NETBSD_TRUE
LIBGO_IS_LINUX_FALSE
LIBGO_IS_LINUX_TRUE
LIBGO_IS_IRIX_FALSE
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 11100 "configure"
+#line 11102 "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 11206 "configure"
+#line 11208 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
*-*-freebsd*) is_freebsd=yes; GOOS=freebsd ;;
*-*-irix6*) is_irix=yes; GOOS=irix ;;
*-*-linux*) is_linux=yes; GOOS=linux ;;
+ *-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;;
*-*-rtems*) is_rtems=yes; GOOS=rtems ;;
*-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
esac
LIBGO_IS_LINUX_FALSE=
fi
+ if test $is_netbsd = yes; then
+ LIBGO_IS_NETBSD_TRUE=
+ LIBGO_IS_NETBSD_FALSE='#'
+else
+ LIBGO_IS_NETBSD_TRUE='#'
+ LIBGO_IS_NETBSD_FALSE=
+fi
+
if test $is_rtems = yes; then
LIBGO_IS_RTEMS_TRUE=
LIBGO_IS_RTEMS_FALSE='#'
as_fn_error "conditional \"LIBGO_IS_LINUX\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${LIBGO_IS_NETBSD_TRUE}" && test -z "${LIBGO_IS_NETBSD_FALSE}"; then
+ as_fn_error "conditional \"LIBGO_IS_NETBSD\" 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
*-*-freebsd*) is_freebsd=yes; GOOS=freebsd ;;
*-*-irix6*) is_irix=yes; GOOS=irix ;;
*-*-linux*) is_linux=yes; GOOS=linux ;;
+ *-*-netbsd*) is_netbsd=yes; GOOS=netbsd ;;
*-*-rtems*) is_rtems=yes; GOOS=rtems ;;
*-*-solaris2*) is_solaris=yes; GOOS=solaris ;;
esac
AM_CONDITIONAL(LIBGO_IS_FREEBSD, test $is_freebsd = yes)
AM_CONDITIONAL(LIBGO_IS_IRIX, test $is_irix = yes)
AM_CONDITIONAL(LIBGO_IS_LINUX, test $is_linux = yes)
+AM_CONDITIONAL(LIBGO_IS_NETBSD, test $is_netbsd = yes)
AM_CONDITIONAL(LIBGO_IS_RTEMS, test $is_rtems = yes)
AM_CONDITIONAL(LIBGO_IS_SOLARIS, test $is_solaris = yes)
AC_SUBST(GOOS)
done := make(chan bool)
for i := 0; i < 5; i++ {
for j, ft := range zt.File {
- go func() {
+ go func(j int, ft ZipTestFile) {
readTestFile(t, ft, z.File[j])
done <- true
- }()
+ }(j, ft)
n++
}
}
)
}
+// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
+// The resolution is 2s.
+// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
+func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
+ t = t.In(time.UTC)
+ fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
+ fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)
+ return
+}
+
// ModTime returns the modification time.
// The resolution is 2s.
func (h *FileHeader) ModTime() time.Time {
return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
}
+// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
+// The resolution is 2s.
+func (h *FileHeader) SetModTime(t time.Time) {
+ h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
+}
+
// traditional names for Unix constants
const (
s_IFMT = 0xf000
"fmt"
"io"
"testing"
+ "time"
)
type stringReaderAt string
}
}
}
+
+func TestModTime(t *testing.T) {
+ var testTime = time.Date(2009, time.November, 10, 23, 45, 58, 0, time.UTC)
+ fh := new(FileHeader)
+ fh.SetModTime(testTime)
+ outTime := fh.ModTime()
+ if !outTime.Equal(testTime) {
+ t.Errorf("times don't match: got %s, want %s", outTime, testTime)
+ }
+}
var data string // test data for write tests
var bytes []byte // test data; same as data but as a slice.
-
func init() {
bytes = make([]byte, N)
for i := 0; i < N; i++ {
for i := 0; i < b.N; i++ {
j := index(buf, 'x')
if j != n-1 {
- println("bad index", j)
- panic("bad index")
+ b.Fatal("bad index", j)
}
}
buf[n-1] = '\x00'
for i := 0; i < b.N; i++ {
eq := equal(buf1, buf2)
if !eq {
- panic("bad equal")
+ b.Fatal("bad equal")
}
}
buf1[n-1] = '\x00'
for i := 0; i < b.N; i++ {
j := index(buf, buf[n-7:])
if j != n-7 {
- println("bad index", j)
- panic("bad index")
+ b.Fatal("bad index", j)
}
}
buf[n-1] = '\x00'
for i := 0; i < b.N; i++ {
j := index(buf, buf[n-7:])
if j != n-7 {
- println("bad index", j)
- panic("bad index")
+ b.Fatal("bad index", j)
}
}
buf[n-1] = '\x00'
for i := 0; i < b.N; i++ {
j := count(buf, buf[n-7:])
if j != 1 {
- println("bad count", j)
- panic("bad count")
+ b.Fatal("bad count", j)
}
}
buf[n-1] = '\x00'
for i := 0; i < b.N; i++ {
j := count(buf, buf[n-7:])
if j != 1 {
- println("bad count", j)
- panic("bad count")
+ b.Fatal("bad count", j)
}
}
buf[n-1] = '\x00'
--- /dev/null
+package bytes_test
+
+import (
+ . "bytes"
+ "encoding/base64"
+ "io"
+ "os"
+)
+
+// Hello world!
+func ExampleBuffer() {
+ var b Buffer // A Buffer needs no initialization.
+ b.Write([]byte("Hello "))
+ b.Write([]byte("world!"))
+ b.WriteTo(os.Stdout)
+}
+
+// Gophers rule!
+func ExampleBuffer_reader() {
+ // A Buffer can turn a string or a []byte into an io.Reader.
+ buf := NewBufferString("R29waGVycyBydWxlIQ==")
+ dec := base64.NewDecoder(base64.StdEncoding, buf)
+ io.Copy(os.Stdout, dec)
+}
tt := encryptTests[0]
c, err := NewCipher(tt.key)
if err != nil {
- panic("NewCipher")
+ b.Fatal("NewCipher:", err)
}
out := make([]byte, len(tt.in))
b.StartTimer()
}
hashes[h] = f
}
+
+// PrivateKey represents a private key using an unspecified algorithm.
+type PrivateKey interface{}
// PublicKeyType is the armor type for a PGP public key.
var PublicKeyType = "PGP PUBLIC KEY BLOCK"
+
// PrivateKeyType is the armor type for a PGP private key.
var PrivateKeyType = "PGP PRIVATE KEY BLOCK"
// 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
+// +build darwin freebsd linux netbsd openbsd
// Unix cryptographically secure pseudorandom number
// generator.
package tls
import (
+ "crypto"
"crypto/rand"
- "crypto/rsa"
"crypto/x509"
"io"
"strings"
// A Certificate is a chain of one or more certificates, leaf first.
type Certificate struct {
Certificate [][]byte
- PrivateKey *rsa.PrivateKey
+ PrivateKey crypto.PrivateKey // supported types: *rsa.PrivateKey
// OCSPStaple contains an optional OCSP response which will be served
// to clients that request it.
OCSPStaple []byte
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, crypto.MD5SHA1, digest)
+ signed, err := rsa.SignPKCS1v15(c.config.rand(), c.config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, digest)
if err != nil {
return c.sendAlert(alertInternalError)
}
ciphertext = ckx.ciphertext[2:]
}
- err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret)
+ err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), ciphertext, preMasterSecret)
if err != nil {
return nil, err
}
copy(serverECDHParams[4:], ecdhePublic)
md5sha1 := md5SHA1Hash(clientHello.random, hello.random, serverECDHParams)
- sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey, crypto.MD5SHA1, md5sha1)
+ sig, err := rsa.SignPKCS1v15(config.rand(), config.Certificates[0].PrivateKey.(*rsa.PrivateKey), crypto.MD5SHA1, md5sha1)
if err != nil {
return nil, errors.New("failed to sign ECDHE parameters: " + err.Error())
}
// Note: We disable -Werror here because the code in this file uses a deprecated API to stay
// compatible with both Mac OS X 10.6 and 10.7. Using a deprecated function on Darwin generates
// a warning.
-#cgo CFLAGS: -Wno-error
+#cgo CFLAGS: -Wno-error -Wno-deprecated-declarations
#cgo LDFLAGS: -framework CoreFoundation -framework Security
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build plan9
+// +build plan9 darwin/nocgo
package tls
// 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) {
+ if s == nil {
+ return
+ }
var candidates []int
if len(cert.AuthorityKeyId) > 0 {
roots []string
currentTime int64
dnsName string
+ nilRoots bool
errorCallback func(*testing.T, int, error) bool
expectedChains [][]string
{
leaf: googleLeaf,
intermediates: []string{thawteIntermediate},
+ nilRoots: true, // verifies that we don't crash
+ currentTime: 1302726541,
+ dnsName: "www.google.com",
+ errorCallback: expectAuthorityUnknown,
+ },
+ {
+ leaf: googleLeaf,
+ intermediates: []string{thawteIntermediate},
roots: []string{verisignRoot},
currentTime: 1,
dnsName: "www.example.com",
DNSName: test.dnsName,
CurrentTime: time.Unix(test.currentTime, 0),
}
+ if test.nilRoots {
+ opts.Roots = nil
+ }
for j, root := range test.roots {
ok := opts.Roots.AppendCertsFromPEM([]byte(root))
// pemCRLPrefix is the magic string that indicates that we have a PEM encoded
// CRL.
var pemCRLPrefix = []byte("-----BEGIN X509 CRL")
+
// pemType is the type of a PEM encoded CRL.
var pemType = "X509 CRL"
func dotest() bool {
// For now, only works on ELF platforms.
- return syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64"
+ // TODO: convert to work with new go tool
+ return false && syscall.OS == "linux" && os.Getenv("GOARCH") == "amd64"
}
func getTable(t *testing.T) *Table {
return n, nil
}
-func BenchmarkRead(b *testing.B) {
+func BenchmarkReadSlice1000Int32s(b *testing.B) {
+ bsr := &byteSliceReader{}
+ slice := make([]int32, 1000)
+ buf := make([]byte, len(slice)*4)
+ b.SetBytes(int64(len(buf)))
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bsr.remain = buf
+ Read(bsr, BigEndian, slice)
+ }
+}
+
+func BenchmarkReadStruct(b *testing.B) {
+ bsr := &byteSliceReader{}
+ var buf bytes.Buffer
+ Write(&buf, BigEndian, &s)
+ n := TotalSize(reflect.ValueOf(s))
+ b.SetBytes(int64(n))
+ t := s
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ bsr.remain = buf.Bytes()
+ Read(bsr, BigEndian, &t)
+ }
+ b.StopTimer()
+ if !reflect.DeepEqual(s, t) {
+ b.Fatal("no match")
+ }
+}
+
+func BenchmarkReadInts(b *testing.B) {
var ls Struct
bsr := &byteSliceReader{}
var r io.Reader = bsr
-
+ b.SetBytes(2 * (1 + 2 + 4 + 8))
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
bsr.remain = big
Read(r, BigEndian, &ls.Int8)
for i := range want.Array {
want.Array[i] = 0
}
+ b.StopTimer()
if !reflect.DeepEqual(ls, want) {
panic("no match")
}
}
-func BenchmarkWrite(b *testing.B) {
+func BenchmarkWriteInts(b *testing.B) {
buf := new(bytes.Buffer)
var w io.Writer = buf
-
+ b.SetBytes(2 * (1 + 2 + 4 + 8))
+ b.ResetTimer()
for i := 0; i < b.N; i++ {
buf.Reset()
- Write(w, BigEndian, &s.Int8)
- Write(w, BigEndian, &s.Int16)
- Write(w, BigEndian, &s.Int32)
- Write(w, BigEndian, &s.Int64)
- Write(w, BigEndian, &s.Uint8)
- Write(w, BigEndian, &s.Uint16)
- Write(w, BigEndian, &s.Uint32)
- Write(w, BigEndian, &s.Uint64)
Write(w, BigEndian, s.Int8)
Write(w, BigEndian, s.Int16)
Write(w, BigEndian, s.Int32)
Write(w, BigEndian, s.Uint32)
Write(w, BigEndian, s.Uint64)
}
-
- if !bytes.Equal(buf.Bytes()[:30], big[:30]) {
- panic("first half doesn't match")
- }
- if !bytes.Equal(buf.Bytes()[30:], big[:30]) {
- panic("second half doesn't match")
+ b.StopTimer()
+ if !bytes.Equal(buf.Bytes(), big[:30]) {
+ b.Fatalf("first half doesn't match: %x %x", buf.Bytes(), big[:30])
}
}
func BenchmarkPutUvarint32(b *testing.B) {
buf := make([]byte, MaxVarintLen32)
+ b.SetBytes(4)
for i := 0; i < b.N; i++ {
for j := uint(0); j < MaxVarintLen32; j++ {
PutUvarint(buf, 1<<(j*7))
func BenchmarkPutUvarint64(b *testing.B) {
buf := make([]byte, MaxVarintLen64)
+ b.SetBytes(8)
for i := 0; i < b.N; i++ {
for j := uint(0); j < MaxVarintLen64; j++ {
PutUvarint(buf, 1<<(j*7))
// The result of encoding a true boolean with field number 7
var boolResult = []byte{0x07, 0x01}
+
// The result of encoding a number 17 with field number 7
var signedResult = []byte{0x07, 2 * 17}
var unsignedResult = []byte{0x07, 17}
var floatResult = []byte{0x07, 0xFE, 0x31, 0x40}
+
// The result of encoding a number 17+19i with field number 7
var complexResult = []byte{0x07, 0xFE, 0x31, 0x40, 0xFE, 0x33, 0x40}
+
// The result of encoding "hello" with field number 7
var bytesResult = []byte{0x07, 0x05, 'h', 'e', 'l', 'l', 'o'}
// isZero returns whether the value is the zero of its type.
func isZero(val reflect.Value) bool {
switch val.Kind() {
- case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ case reflect.Array:
+ for i := 0; i < val.Len(); i++ {
+ if !isZero(val.Index(i)) {
+ return false
+ }
+ }
+ return true
+ case reflect.Map, reflect.Slice, reflect.String:
return val.Len() == 0
case reflect.Bool:
return !val.Bool()
return val.Float() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return val.Uint() == 0
+ case reflect.Struct:
+ for i := 0; i < val.NumField(); i++ {
+ if !isZero(val.Field(i)) {
+ return false
+ }
+ }
+ return true
}
panic("unknown type in isZero " + val.Type().String())
}
"io"
"strings"
"testing"
+ "time"
)
// Types that implement the GobEncoder/Decoder interfaces.
t.Errorf("got = %q, want %q", got, gdb)
}
}
+
+// Another bug: this caused a crash with the new Go1 Time type.
+// We throw in a gob-encoding array, to test another case of isZero
+
+type isZeroBug struct {
+ T time.Time
+ S string
+ I int
+ A isZeroBugArray
+}
+
+type isZeroBugArray [2]uint8
+
+// Receiver is value, not pointer, to test isZero of array.
+func (a isZeroBugArray) GobEncode() (b []byte, e error) {
+ b = append(b, a[:]...)
+ return b, nil
+}
+
+func (a *isZeroBugArray) GobDecode(data []byte) error {
+ println("DECODE")
+ if len(data) != len(a) {
+ return io.EOF
+ }
+ a[0] = data[0]
+ a[1] = data[1]
+ return nil
+}
+
+func TestGobEncodeIsZero(t *testing.T) {
+ x := isZeroBug{time.Now(), "hello", -55, isZeroBugArray{1, 2}}
+ b := new(bytes.Buffer)
+ enc := NewEncoder(b)
+ err := enc.Encode(x)
+ if err != nil {
+ t.Fatal("encode:", err)
+ }
+ var y isZeroBug
+ dec := NewDecoder(b)
+ err = dec.Decode(&y)
+ if err != nil {
+ t.Fatal("decode:", err)
+ }
+ if x != y {
+ t.Fatalf("%v != %v", x, y)
+ }
+}
func BenchmarkEndToEndPipe(b *testing.B) {
r, w, err := os.Pipe()
if err != nil {
- panic("can't get pipe:" + err.Error())
+ b.Fatal("can't get pipe:", err)
}
benchmarkEndToEnd(r, w, b)
}
}
return ut
}
+
// A typeId represents a gob Type as an integer that can be passed on the wire.
// Internally, typeIds are used as keys to a map to recover the underlying type info.
type typeId int32
enc := NewEncoder(ioutil.Discard)
for i := 0; i < b.N; i++ {
if err := enc.Encode(&codeStruct); err != nil {
- panic(err)
+ b.Fatal("Encode:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
}
for i := 0; i < b.N; i++ {
if _, err := Marshal(&codeStruct); err != nil {
- panic(err)
+ b.Fatal("Marshal:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
buf.WriteByte('\n')
buf.WriteByte('\n')
if err := dec.Decode(&r); err != nil {
- panic(err)
+ b.Fatal("Decode:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
for i := 0; i < b.N; i++ {
var r codeResponse
if err := Unmarshal(codeJSON, &r); err != nil {
- panic(err)
+ b.Fatal("Unmmarshal:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
var r codeResponse
for i := 0; i < b.N; i++ {
if err := Unmarshal(codeJSON, &r); err != nil {
- panic(err)
+ b.Fatal("Unmmarshal:", err)
}
}
b.SetBytes(int64(len(codeJSON)))
// Feed in an empty string - the shortest, simplest value -
// so that it knows we got to the end of the value.
if d.scan.redo {
- panic("redo")
+ // rewind.
+ d.scan.redo = false
+ d.scan.step = stateBeginValue
}
d.scan.step(&d.scan, '"')
d.scan.step(&d.scan, '"')
}
v = pv
- // Decoding into nil interface? Switch to non-reflect code.
- iv := v
- ok := iv.Kind() == reflect.Interface
- if ok {
- iv.Set(reflect.ValueOf(d.arrayInterface()))
- return
- }
-
// Check type of target.
- av := v
- if av.Kind() != reflect.Array && av.Kind() != reflect.Slice {
+ switch v.Kind() {
+ default:
d.saveError(&UnmarshalTypeError{"array", v.Type()})
d.off--
d.next()
return
+ case reflect.Interface:
+ // Decoding into nil interface? Switch to non-reflect code.
+ v.Set(reflect.ValueOf(d.arrayInterface()))
+ return
+ case reflect.Array:
+ case reflect.Slice:
+ break
}
- sv := v
-
i := 0
for {
// Look ahead for ] - can only happen on first iteration.
d.scan.undo(op)
// Get element of array, growing if necessary.
- if i >= av.Cap() && sv.IsValid() {
- newcap := sv.Cap() + sv.Cap()/2
- if newcap < 4 {
- newcap = 4
+ if v.Kind() == reflect.Slice {
+ // Grow slice if necessary
+ if i >= v.Cap() {
+ newcap := v.Cap() + v.Cap()/2
+ if newcap < 4 {
+ newcap = 4
+ }
+ newv := reflect.MakeSlice(v.Type(), v.Len(), newcap)
+ reflect.Copy(newv, v)
+ v.Set(newv)
+ }
+ if i >= v.Len() {
+ v.SetLen(i + 1)
}
- newv := reflect.MakeSlice(sv.Type(), sv.Len(), newcap)
- reflect.Copy(newv, sv)
- sv.Set(newv)
- }
- if i >= av.Len() && sv.IsValid() {
- // Must be slice; gave up on array during i >= av.Cap().
- sv.SetLen(i + 1)
}
- // Decode into element.
- if i < av.Len() {
- d.value(av.Index(i))
+ if i < v.Len() {
+ // Decode into element.
+ d.value(v.Index(i))
} else {
// Ran out of fixed array: skip.
d.value(reflect.Value{})
}
}
- if i < av.Len() {
- if !sv.IsValid() {
+ if i < v.Len() {
+ if v.Kind() == reflect.Array {
// Array. Zero the rest.
- z := reflect.Zero(av.Type().Elem())
- for ; i < av.Len(); i++ {
- av.Index(i).Set(z)
+ z := reflect.Zero(v.Type().Elem())
+ for ; i < v.Len(); i++ {
+ v.Index(i).Set(z)
}
} else {
- sv.SetLen(i)
+ v.SetLen(i)
}
}
- if i == 0 && av.Kind() == reflect.Slice && sv.IsNil() {
- sv.Set(reflect.MakeSlice(sv.Type(), 0, 0))
+ if i == 0 && v.Kind() == reflect.Slice {
+ v.Set(reflect.MakeSlice(v.Type(), 0, 0))
}
}
import (
"bytes"
+ "fmt"
"reflect"
"strings"
"testing"
// syntax errors
{`{"X": "foo", "Y"}`, nil, nil, &SyntaxError{"invalid character '}' after object key", 17}},
+ {`[1, 2, 3+]`, nil, nil, &SyntaxError{"invalid character '+' after array element", 9}},
+
+ // array tests
+ {`[1, 2, 3]`, new([3]int), [3]int{1, 2, 3}, nil},
+ {`[1, 2, 3]`, new([1]int), [1]int{1}, nil},
+ {`[1, 2, 3]`, new([5]int), [5]int{1, 2, 3, 0, 0}, nil},
// composite tests
{allValueIndent, new(All), allValue, nil},
}
}
+// WrongString is a struct that's misusing the ,string modifier.
+type WrongString struct {
+ Message string `json:"result,string"`
+}
+
+type wrongStringTest struct {
+ in, err string
+}
+
+// TODO(bradfitz): as part of Issue 2331, fix these tests' expected
+// error values to be helpful, rather than the confusing messages they
+// are now.
+var wrongStringTests = []wrongStringTest{
+ {`{"result":"x"}`, "JSON decoder out of sync - data changing underfoot?"},
+ {`{"result":"foo"}`, "json: cannot unmarshal bool into Go value of type string"},
+ {`{"result":"123"}`, "json: cannot unmarshal number into Go value of type string"},
+}
+
+// If people misuse the ,string modifier, the error message should be
+// helpful, telling the user that they're doing it wrong.
+func TestErrorMessageFromMisusedString(t *testing.T) {
+ for n, tt := range wrongStringTests {
+ r := strings.NewReader(tt.in)
+ var s WrongString
+ err := NewDecoder(r).Decode(&s)
+ got := fmt.Sprintf("%v", err)
+ if got != tt.err {
+ t.Errorf("%d. got err = %q, want %q", n, got, tt.err)
+ }
+ }
+}
+
func noSpace(c rune) rune {
if isSpace(c) {
return -1
// An encodeState encodes JSON into a bytes.Buffer.
type encodeState struct {
bytes.Buffer // accumulated output
+ scratch [64]byte
}
func (e *encodeState) marshal(v interface{}) (err error) {
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- writeString(e, strconv.FormatInt(v.Int(), 10))
-
+ b := strconv.AppendInt(e.scratch[:0], v.Int(), 10)
+ if quoted {
+ writeString(e, string(b))
+ } else {
+ e.Write(b)
+ }
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- writeString(e, strconv.FormatUint(v.Uint(), 10))
-
+ b := strconv.AppendUint(e.scratch[:0], v.Uint(), 10)
+ if quoted {
+ writeString(e, string(b))
+ } else {
+ e.Write(b)
+ }
case reflect.Float32, reflect.Float64:
- writeString(e, strconv.FormatFloat(v.Float(), 'g', -1, v.Type().Bits()))
-
+ b := strconv.AppendFloat(e.scratch[:0], v.Float(), 'g', -1, v.Type().Bits())
+ if quoted {
+ writeString(e, string(b))
+ } else {
+ e.Write(b)
+ }
case reflect.String:
if quoted {
sb, err := Marshal(v.String())
if err != nil {
t.Errorf("#%d: unexpected error: %#v", i, err)
} else if got, want := dest, test.Value; !reflect.DeepEqual(got, want) {
- t.Errorf("#%d: unmarshal(%#s) = %#v, want %#v", i, test.ExpectXML, got, want)
+ t.Errorf("#%d: unmarshal(%q) = %#v, want %#v", i, test.ExpectXML, got, want)
}
}
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
package inotify
import (
t.Fatalf("NewWatcher() failed: %s", err)
}
+ t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO
+ return
+
// Add a watch for "_test"
err = watcher.Watch("_test")
if err != nil {
// it panics.
func Register(name string, driver driver.Driver) {
if driver == nil {
- panic("db: Register driver is nil")
+ panic("sql: Register driver is nil")
}
if _, dup := drivers[name]; dup {
- panic("db: Register called twice for driver " + name)
+ panic("sql: Register called twice for driver " + name)
}
drivers[name] = driver
}
// ErrNoRows is returned by Scan when QueryRow doesn't return a
// row. In such a case, QueryRow returns a placeholder *Row value that
// defers this error until a Scan.
-var ErrNoRows = errors.New("db: no rows in result set")
+var ErrNoRows = errors.New("sql: no rows in result set")
// DB is a database handle. It's safe for concurrent use by multiple
// goroutines.
func Open(driverName, dataSourceName string) (*DB, error) {
driver, ok := drivers[driverName]
if !ok {
- return nil, fmt.Errorf("db: unknown driver %q (forgotten import?)", driverName)
+ return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
}
return &DB{driver: driver, dsn: dataSourceName}, nil
}
// placeholders, so we won't sanity check input here and instead let the
// driver deal with errors.
if want := si.NumInput(); want != -1 && len(args) != want {
- return nil, fmt.Errorf("db: expected %d arguments, got %d", want, len(args))
+ return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args))
}
// Convert args to subset types.
for n, arg := range args {
args[n], err = cc.ColumnConverter(n).ConvertValue(arg)
if err != nil {
- return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+ return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
}
if !driver.IsParameterSubsetType(args[n]) {
- return nil, fmt.Errorf("db: driver ColumnConverter error converted %T to unsupported type %T",
+ return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
arg, args[n])
}
}
for n, arg := range args {
args[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
if err != nil {
- return nil, fmt.Errorf("db: converting Exec argument #%d's type: %v", n, err)
+ return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
}
}
}
s.mu.Lock()
if s.closed {
s.mu.Unlock()
- err = errors.New("db: statement is closed")
+ err = errors.New("sql: statement is closed")
return
}
// placeholders, so we won't sanity check input here and instead let the
// driver deal with errors.
if want := si.NumInput(); want != -1 && len(args) != want {
- return nil, fmt.Errorf("db: statement expects %d inputs; got %d", si.NumInput(), len(args))
+ return nil, fmt.Errorf("sql: statement expects %d inputs; got %d", si.NumInput(), len(args))
}
sargs, err := subsetTypeArgs(args)
if err != nil {
return rs.lasterr
}
+// Columns returns the column names.
+// Columns returns an error if the rows are closed, or if the rows
+// are from QueryRow and there was a deferred error.
+func (rs *Rows) Columns() ([]string, error) {
+ if rs.closed {
+ return nil, errors.New("sql: Rows are closed")
+ }
+ if rs.rowsi == nil {
+ return nil, errors.New("sql: no Rows available")
+ }
+ return rs.rowsi.Columns(), nil
+}
+
// Scan copies the columns in the current row into the values pointed
// at by dest. If dest contains pointers to []byte, the slices should
// not be modified and should only be considered valid until the next
// call to Next or Scan.
func (rs *Rows) Scan(dest ...interface{}) error {
if rs.closed {
- return errors.New("db: Rows closed")
+ return errors.New("sql: Rows closed")
}
if rs.lasterr != nil {
return rs.lasterr
}
if rs.lastcols == nil {
- return errors.New("db: Scan called without calling Next")
+ return errors.New("sql: Scan called without calling Next")
}
if len(dest) != len(rs.lastcols) {
- return fmt.Errorf("db: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
+ return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
}
for i, sv := range rs.lastcols {
err := convertAssign(dest[i], sv)
if err != nil {
- return fmt.Errorf("db: Scan error on column index %d: %v", i, err)
+ return fmt.Errorf("sql: Scan error on column index %d: %v", i, err)
}
}
return nil
}
}
+func TestRowsColumns(t *testing.T) {
+ db := newTestDB(t, "people")
+ defer closeDB(t, db)
+ rows, err := db.Query("SELECT|people|age,name|")
+ if err != nil {
+ t.Fatalf("Query: %v", err)
+ }
+ cols, err := rows.Columns()
+ if err != nil {
+ t.Fatalf("Columns: %v", err)
+ }
+ want := []string{"age", "name"}
+ if !reflect.DeepEqual(cols, want) {
+ t.Errorf("got %#v; want %#v", cols, want)
+ }
+}
+
func TestQueryRow(t *testing.T) {
db := newTestDB(t, "people")
defer closeDB(t, db)
{[]interface{}{7, 9}, ""},
// Invalid conversions:
- {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "db: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
- {[]interface{}{"Brad", "strconv fail"}, "db: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
+ {[]interface{}{"Brad", int64(0xFFFFFFFF)}, "sql: converting Exec argument #1's type: sql/driver: value 4294967295 overflows int32"},
+ {[]interface{}{"Brad", "strconv fail"}, "sql: converting Exec argument #1's type: sql/driver: value \"strconv fail\" can't be converted to int32"},
// Wrong number of args:
- {[]interface{}{}, "db: expected 2 arguments, got 0"},
- {[]interface{}{1, 2, 3}, "db: expected 2 arguments, got 3"},
+ {[]interface{}{}, "sql: expected 2 arguments, got 0"},
+ {[]interface{}{1, 2, 3}, "sql: expected 2 arguments, got 3"},
}
for n, et := range execTests {
_, err := stmt.Exec(et.args...)
return "publickey"
}
-// ClientAuthPublickey returns a ClientAuth using public key authentication.
-func ClientAuthPublickey(impl ClientKeyring) ClientAuth {
+// ClientAuthKeyring returns a ClientAuth using public key authentication.
+func ClientAuthKeyring(impl ClientKeyring) ClientAuth {
return &publickeyAuth{impl}
}
PasswordCallback: func(user, pass string) bool {
return user == "testuser" && pass == string(clientPassword)
},
- PubKeyCallback: func(user, algo string, pubkey []byte) bool {
+ PublicKeyCallback: func(user, algo string, pubkey []byte) bool {
key := clientKeychain.keys[0].(*rsa.PrivateKey).PublicKey
expected := []byte(serializePublickey(key))
algoname := algoName(key)
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
- ClientAuthPublickey(clientKeychain),
+ ClientAuthKeyring(clientKeychain),
},
}
c, err := Dial("tcp", newMockAuthServer(t), config)
User: "testuser",
Auth: []ClientAuth{
ClientAuthPassword(wrongPw),
- ClientAuthPublickey(clientKeychain),
+ ClientAuthKeyring(clientKeychain),
},
}
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
- ClientAuthPublickey(kc),
+ ClientAuthKeyring(kc),
},
}
config := &ClientConfig{
User: "testuser",
Auth: []ClientAuth{
- ClientAuthPublickey(kc),
+ ClientAuthKeyring(kc),
},
}
c, err := Dial("tcp", newMockAuthServer(t), config)
config := &ClientConfig{
User: *sshuser,
Auth: []ClientAuth{
- ClientAuthPublickey(kc),
+ ClientAuthKeyring(kc),
},
}
conn, err := Dial("tcp", "localhost:22", config)
// several goroutines.
PasswordCallback func(user, password string) bool
- // PubKeyCallback, if non-nil, is called when a client attempts public
+ // PublicKeyCallback, if non-nil, is called when a client attempts public
// key authentication. It must return true iff the given public key is
// valid for the given user.
- PubKeyCallback func(user, algo string, pubkey []byte) bool
+ PublicKeyCallback func(user, algo string, pubkey []byte) bool
// Cryptographic-related configuration.
Crypto CryptoConfig
// testPubKey returns true if the given public key is acceptable for the user.
func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool {
- if s.config.PubKeyCallback == nil || !isAcceptableAlgo(algo) {
+ if s.config.PublicKeyCallback == nil || !isAcceptableAlgo(algo) {
return false
}
}
}
- result := s.config.PubKeyCallback(user, algo, pubKey)
+ result := s.config.PublicKeyCallback(user, algo, pubKey)
if len(s.cachedPubKeys) < maxCachedPubKeys {
c := cachedPubKey{
user: user,
break userAuthLoop
}
case "publickey":
- if s.config.PubKeyCallback == nil {
+ if s.config.PublicKeyCallback == nil {
break
}
payload := userAuthReq.Payload
if s.config.PasswordCallback != nil {
failureMsg.Methods = append(failureMsg.Methods, "password")
}
- if s.config.PubKeyCallback != nil {
+ if s.config.PublicKeyCallback != nil {
failureMsg.Methods = append(failureMsg.Methods, "publickey")
}
*clientChan // the channel backing this session
- started bool // true once Start, Run or Shell is invoked.
- closeAfterWait []io.Closer
- copyFuncs []func() error
- errch chan error // one send per copyFunc
+ started bool // true once Start, Run or Shell is invoked.
+ copyFuncs []func() error
+ errch chan error // one send per copyFunc
+
+ // true if pipe method is active
+ stdinpipe, stdoutpipe, stderrpipe bool
}
// RFC 4254 Section 6.4.
func (s *Session) start() error {
s.started = true
- type F func(*Session) error
+ type F func(*Session)
for _, setupFd := range []F{(*Session).stdin, (*Session).stdout, (*Session).stderr} {
- if err := setupFd(s); err != nil {
- return err
- }
+ setupFd(s)
}
s.errch = make(chan error, len(s.copyFuncs))
copyError = err
}
}
- for _, fd := range s.closeAfterWait {
- fd.Close()
- }
if waitErr != nil {
return waitErr
}
return &ExitError{wm}
}
-func (s *Session) stdin() error {
+func (s *Session) stdin() {
+ if s.stdinpipe {
+ return
+ }
if s.Stdin == nil {
s.Stdin = new(bytes.Buffer)
}
}
return err
})
- return nil
}
-func (s *Session) stdout() error {
+func (s *Session) stdout() {
+ if s.stdoutpipe {
+ return
+ }
if s.Stdout == nil {
s.Stdout = ioutil.Discard
}
_, err := io.Copy(s.Stdout, s.clientChan.stdout)
return err
})
- return nil
}
-func (s *Session) stderr() error {
+func (s *Session) stderr() {
+ if s.stderrpipe {
+ return
+ }
if s.Stderr == nil {
s.Stderr = ioutil.Discard
}
_, err := io.Copy(s.Stderr, s.clientChan.stderr)
return err
})
- return nil
}
// StdinPipe returns a pipe that will be connected to the
if s.started {
return nil, errors.New("ssh: StdinPipe after process started")
}
- pr, pw := io.Pipe()
- s.Stdin = pr
- s.closeAfterWait = append(s.closeAfterWait, pr)
- return pw, nil
+ s.stdinpipe = true
+ return s.clientChan.stdin, nil
}
// StdoutPipe returns a pipe that will be connected to the
// stdout and stderr streams. If the StdoutPipe reader is
// not serviced fast enought it may eventually cause the
// remote command to block.
-func (s *Session) StdoutPipe() (io.ReadCloser, error) {
+func (s *Session) StdoutPipe() (io.Reader, error) {
if s.Stdout != nil {
return nil, errors.New("ssh: Stdout already set")
}
if s.started {
return nil, errors.New("ssh: StdoutPipe after process started")
}
- pr, pw := io.Pipe()
- s.Stdout = pw
- s.closeAfterWait = append(s.closeAfterWait, pw)
- return pr, nil
+ s.stdoutpipe = true
+ return s.clientChan.stdout, nil
}
// StderrPipe returns a pipe that will be connected to the
// stdout and stderr streams. If the StderrPipe reader is
// not serviced fast enought it may eventually cause the
// remote command to block.
-func (s *Session) StderrPipe() (io.ReadCloser, error) {
+func (s *Session) StderrPipe() (io.Reader, error) {
if s.Stderr != nil {
return nil, errors.New("ssh: Stderr already set")
}
if s.started {
return nil, errors.New("ssh: StderrPipe after process started")
}
- pr, pw := io.Pipe()
- s.Stderr = pw
- s.closeAfterWait = append(s.closeAfterWait, pw)
- return pr, nil
+ s.stderrpipe = true
+ return s.clientChan.stderr, nil
}
// TODO(dfc) add Output and CombinedOutput helpers
serverConfig.PasswordCallback = func(user, pass string) bool {
return user == "testuser" && pass == string(pw)
}
- serverConfig.PubKeyCallback = nil
+ serverConfig.PublicKeyCallback = nil
l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
if err != nil {
"io"
"net"
)
+
// Dial initiates a connection to the addr from the remote host.
// addr is resolved using net.ResolveTCPAddr before connection.
// This could allow an observer to observe the DNS name of the
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
package terminal
-import "io"
+import (
+ "io"
+ "sync"
+)
+
+// EscapeCodes contains escape sequences that can be written to the terminal in
+// order to achieve different styles of text.
+type EscapeCodes struct {
+ // Foreground colors
+ Black, Red, Green, Yellow, Blue, Magenta, Cyan, White []byte
+
+ // Reset all attributes
+ Reset []byte
+}
+
+var vt100EscapeCodes = EscapeCodes{
+ Black: []byte{keyEscape, '[', '3', '0', 'm'},
+ Red: []byte{keyEscape, '[', '3', '1', 'm'},
+ Green: []byte{keyEscape, '[', '3', '2', 'm'},
+ Yellow: []byte{keyEscape, '[', '3', '3', 'm'},
+ Blue: []byte{keyEscape, '[', '3', '4', 'm'},
+ Magenta: []byte{keyEscape, '[', '3', '5', 'm'},
+ Cyan: []byte{keyEscape, '[', '3', '6', 'm'},
+ White: []byte{keyEscape, '[', '3', '7', 'm'},
+
+ Reset: []byte{keyEscape, '[', '0', 'm'},
+}
// Terminal contains the state for running a VT100 terminal that is capable of
// reading lines of input.
type Terminal struct {
+ // AutoCompleteCallback, if non-null, is called for each keypress
+ // with the full input line and the current position of the cursor.
+ // If it returns a nil newLine, the key press is processed normally.
+ // Otherwise it returns a replacement line and the new cursor position.
+ AutoCompleteCallback func(line []byte, pos, key int) (newLine []byte, newPos int)
+
+ // Escape contains a pointer to the escape codes for this terminal.
+ // It's always a valid pointer, although the escape codes themselves
+ // may be empty if the terminal doesn't support them.
+ Escape *EscapeCodes
+
+ // lock protects the terminal and the state in this object from
+ // concurrent processing of a key press and a Write() call.
+ lock sync.Mutex
+
c io.ReadWriter
prompt string
line []byte
// pos is the logical position of the cursor in line
pos int
+ // echo is true if local echo is enabled
+ echo bool
// cursorX contains the current X value of the cursor where the left
// edge is 0. cursorY contains the row number where the first row of
// "> ").
func NewTerminal(c io.ReadWriter, prompt string) *Terminal {
return &Terminal{
+ Escape: &vt100EscapeCodes,
c: c,
prompt: prompt,
termWidth: 80,
termHeight: 24,
+ echo: true,
}
}
// queue appends data to the end of t.outBuf
func (t *Terminal) queue(data []byte) {
- if len(t.outBuf)+len(data) > cap(t.outBuf) {
- newOutBuf := make([]byte, len(t.outBuf), 2*(len(t.outBuf)+len(data)))
- copy(newOutBuf, t.outBuf)
- t.outBuf = newOutBuf
- }
-
- oldLen := len(t.outBuf)
- t.outBuf = t.outBuf[:len(t.outBuf)+len(data)]
- copy(t.outBuf[oldLen:], data)
+ t.outBuf = append(t.outBuf, data...)
}
var eraseUnderCursor = []byte{' ', keyEscape, '[', 'D'}
+var space = []byte{' '}
func isPrintable(key int) bool {
return key >= 32 && key < 127
// moveCursorToPos appends data to t.outBuf which will move the cursor to the
// given, logical position in the text.
func (t *Terminal) moveCursorToPos(pos int) {
+ if !t.echo {
+ return
+ }
+
x := len(t.prompt) + pos
y := x / t.termWidth
x = x % t.termWidth
right = x - t.cursorX
}
+ t.cursorX = x
+ t.cursorY = y
+ t.move(up, down, left, right)
+}
+
+func (t *Terminal) move(up, down, left, right int) {
movement := make([]byte, 3*(up+down+left+right))
m := movement
for i := 0; i < up; i++ {
m = m[3:]
}
- t.cursorX = x
- t.cursorY = y
t.queue(movement)
}
+func (t *Terminal) clearLineToRight() {
+ op := []byte{keyEscape, '[', 'K'}
+ t.queue(op)
+}
+
const maxLineLength = 4096
// handleKey processes the given key and, optionally, returns a line of text
return
}
t.pos--
+ t.moveCursorToPos(t.pos)
copy(t.line[t.pos:], t.line[1+t.pos:])
t.line = t.line[:len(t.line)-1]
- t.writeLine(t.line[t.pos:])
- t.moveCursorToPos(t.pos)
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ }
t.queue(eraseUnderCursor)
+ t.moveCursorToPos(t.pos)
case keyAltLeft:
// move left by a word.
if t.pos == 0 {
t.cursorY = 0
t.maxLine = 0
default:
+ if t.AutoCompleteCallback != nil {
+ t.lock.Unlock()
+ newLine, newPos := t.AutoCompleteCallback(t.line, t.pos, key)
+ t.lock.Lock()
+
+ if newLine != nil {
+ if t.echo {
+ t.moveCursorToPos(0)
+ t.writeLine(newLine)
+ for i := len(newLine); i < len(t.line); i++ {
+ t.writeLine(space)
+ }
+ t.moveCursorToPos(newPos)
+ }
+ t.line = newLine
+ t.pos = newPos
+ return
+ }
+ }
if !isPrintable(key) {
return
}
t.line = t.line[:len(t.line)+1]
copy(t.line[t.pos+1:], t.line[t.pos:])
t.line[t.pos] = byte(key)
- t.writeLine(t.line[t.pos:])
+ if t.echo {
+ t.writeLine(t.line[t.pos:])
+ }
t.pos++
t.moveCursorToPos(t.pos)
}
func (t *Terminal) writeLine(line []byte) {
for len(line) != 0 {
- if t.cursorX == t.termWidth {
- t.queue([]byte("\r\n"))
- t.cursorX = 0
- t.cursorY++
- if t.cursorY > t.maxLine {
- t.maxLine = t.cursorY
- }
- }
-
remainingOnLine := t.termWidth - t.cursorX
todo := len(line)
if todo > remainingOnLine {
t.queue(line[:todo])
t.cursorX += todo
line = line[todo:]
+
+ if t.cursorX == t.termWidth {
+ t.cursorX = 0
+ t.cursorY++
+ if t.cursorY > t.maxLine {
+ t.maxLine = t.cursorY
+ }
+ }
}
}
func (t *Terminal) Write(buf []byte) (n int, err error) {
- return t.c.Write(buf)
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ if t.cursorX == 0 && t.cursorY == 0 {
+ // This is the easy case: there's nothing on the screen that we
+ // have to move out of the way.
+ return t.c.Write(buf)
+ }
+
+ // We have a prompt and possibly user input on the screen. We
+ // have to clear it first.
+ t.move(0, /* up */ 0, /* down */ t.cursorX, /* left */ 0 /* right */ )
+ t.cursorX = 0
+ t.clearLineToRight()
+
+ for t.cursorY > 0 {
+ t.move(1, /* up */ 0, 0, 0)
+ t.cursorY--
+ t.clearLineToRight()
+ }
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+
+ if n, err = t.c.Write(buf); err != nil {
+ return
+ }
+
+ t.queue([]byte(t.prompt))
+ chars := len(t.prompt)
+ if t.echo {
+ t.queue(t.line)
+ chars += len(t.line)
+ }
+ t.cursorX = chars % t.termWidth
+ t.cursorY = chars / t.termWidth
+ t.moveCursorToPos(t.pos)
+
+ if _, err = t.c.Write(t.outBuf); err != nil {
+ return
+ }
+ t.outBuf = t.outBuf[:0]
+ return
+}
+
+// ReadPassword temporarily changes the prompt and reads a password, without
+// echo, from the terminal.
+func (t *Terminal) ReadPassword(prompt string) (line string, err error) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ oldPrompt := t.prompt
+ t.prompt = prompt
+ t.echo = false
+
+ line, err = t.readLine()
+
+ t.prompt = oldPrompt
+ t.echo = true
+
+ return
}
// ReadLine returns a line of input from the terminal.
func (t *Terminal) ReadLine() (line string, err error) {
- if t.cursorX == 0 {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ return t.readLine()
+}
+
+func (t *Terminal) readLine() (line string, err error) {
+ // t.lock must be held at this point
+
+ if t.cursorX == 0 && t.cursorY == 0 {
t.writeLine([]byte(t.prompt))
t.c.Write(t.outBuf)
t.outBuf = t.outBuf[:0]
// containing a partial key sequence
readBuf := t.inBuf[len(t.remainder):]
var n int
+
+ t.lock.Unlock()
n, err = t.c.Read(readBuf)
+ t.lock.Lock()
+
if err != nil {
return
}
}
func (t *Terminal) SetSize(width, height int) {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
t.termWidth, t.termHeight = width, height
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
package terminal
import (
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build linux
+
// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
//
// oldState, err := terminal.MakeRaw(0)
// if err != nil {
-// panic(err.String())
+// panic(err)
// }
// defer terminal.Restore(0, oldState)
package terminal
import (
"io"
"syscall"
+ "unsafe"
)
// State contains the state of a terminal.
return err
}
+func ioctl(int, int, unsafe.Pointer) int __asm__("ioctl")
+
+// GetSize returns the dimensions of the given terminal.
+func GetSize(fd int) (width, height int, err error) {
+ var dimensions [4]uint16
+
+ if ioctl(fd, syscall.TIOCGWINSZ, unsafe.Pointer(&dimensions)) < 0 {
+ return -1, -1, syscall.GetErrno()
+ }
+ return int(dimensions[1]), int(dimensions[0]), nil
+}
+
// ReadPassword reads a line of input from a terminal without local echo. This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
// Package winfsnotify allows the user to receive
// file system event notifications on Windows.
package winfsnotify
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
package winfsnotify
import (
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
package main
import (
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build windows
+
package main
import (
+// +build windows
// mksyscall_windows.pl winapi.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
}
}
+var mallocBuf bytes.Buffer
+
+var mallocTest = []struct {
+ count int
+ desc string
+ fn func()
+}{
+ {0, `Sprintf("")`, func() { Sprintf("") }},
+ {1, `Sprintf("xxx")`, func() { Sprintf("xxx") }},
+ {1, `Sprintf("%x")`, func() { Sprintf("%x", 7) }},
+ {2, `Sprintf("%s")`, func() { Sprintf("%s", "hello") }},
+ {1, `Sprintf("%x %x")`, func() { Sprintf("%x", 7, 112) }},
+ {1, `Sprintf("%g")`, func() { Sprintf("%g", 3.14159) }},
+ {0, `Fprintf(buf, "%x %x %x")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%x %x %x", 7, 8, 9) }},
+ {1, `Fprintf(buf, "%s")`, func() { mallocBuf.Reset(); Fprintf(&mallocBuf, "%s", "hello") }},
+}
+
+var _ bytes.Buffer
+
func TestCountMallocs(t *testing.T) {
if testing.Short() {
return
}
- const N = 100
- runtime.UpdateMemStats()
- mallocs := 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- Sprintf("")
- }
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Sprintf(\"\"): %d\n", mallocs/N)
- runtime.UpdateMemStats()
- mallocs = 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- Sprintf("xxx")
- }
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Sprintf(\"xxx\"): %d\n", mallocs/N)
- runtime.UpdateMemStats()
- mallocs = 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- Sprintf("%x", i)
- }
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Sprintf(\"%%x\"): %d\n", mallocs/N)
- runtime.UpdateMemStats()
- mallocs = 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- Sprintf("%s", "hello")
- }
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Sprintf(\"%%s\"): %d\n", mallocs/N)
- runtime.UpdateMemStats()
- mallocs = 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- Sprintf("%x %x", i, i)
- }
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Sprintf(\"%%x %%x\"): %d\n", mallocs/N)
- runtime.UpdateMemStats()
- mallocs = 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- Sprintf("%g", 3.14159)
- }
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Sprintf(\"%%g\"): %d\n", mallocs/N)
- buf := new(bytes.Buffer)
- runtime.UpdateMemStats()
- mallocs = 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- buf.Reset()
- Fprintf(buf, "%x %x %x", i, i, i)
- }
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Fprintf(buf, \"%%x %%x %%x\"): %d\n", mallocs/N)
- runtime.UpdateMemStats()
- mallocs = 0 - runtime.MemStats.Mallocs
- for i := 0; i < N; i++ {
- buf.Reset()
- Fprintf(buf, "%s", "hello")
+ for _, mt := range mallocTest {
+ const N = 100
+ runtime.UpdateMemStats()
+ mallocs := 0 - runtime.MemStats.Mallocs
+ for i := 0; i < N; i++ {
+ mt.fn()
+ }
+ runtime.UpdateMemStats()
+ mallocs += runtime.MemStats.Mallocs
+ if mallocs/N != uint64(mt.count) {
+ t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
+ }
}
- runtime.UpdateMemStats()
- mallocs += runtime.MemStats.Mallocs
- Printf("mallocs per Fprintf(buf, \"%%s\"): %d\n", mallocs/N)
}
type flagPrinter struct{}
return i - 1
}
+var (
+ trueBytes = []byte("true")
+ falseBytes = []byte("false")
+)
+
// fmt_boolean formats a boolean.
func (f *fmt) fmt_boolean(v bool) {
if v {
- f.padString("true")
+ f.pad(trueBytes)
} else {
- f.padString("false")
+ f.pad(falseBytes)
}
}
}
// fmt_sx formats a string as a hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sx(s string) {
- t := ""
+func (f *fmt) fmt_sx(s, digits string) {
+ // TODO: Avoid buffer by pre-padding.
+ var b bytes.Buffer
for i := 0; i < len(s); i++ {
if i > 0 && f.space {
- t += " "
+ b.WriteByte(' ')
}
v := s[i]
- t += string(ldigits[v>>4])
- t += string(ldigits[v&0xF])
+ b.WriteByte(digits[v>>4])
+ b.WriteByte(digits[v&0xF])
}
- f.padString(t)
-}
-
-// fmt_sX formats a string as an uppercase hexadecimal encoding of its bytes.
-func (f *fmt) fmt_sX(s string) {
- t := ""
- for i := 0; i < len(s); i++ {
- if i > 0 && f.space {
- t += " "
- }
- v := s[i]
- t += string(udigits[v>>4])
- t += string(udigits[v&0xF])
- }
- f.padString(t)
+ f.pad(b.Bytes())
}
// fmt_q formats a string as a double-quoted, escaped Go string constant.
// fmt_qc formats the integer as a single-quoted, escaped Go character constant.
// If the character is not valid Unicode, it will print '\ufffd'.
func (f *fmt) fmt_qc(c int64) {
- var quoted string
+ var quoted []byte
if f.plus {
- quoted = strconv.QuoteRuneToASCII(rune(c))
+ quoted = strconv.AppendQuoteRuneToASCII(f.intbuf[0:0], rune(c))
} else {
- quoted = strconv.QuoteRune(rune(c))
+ quoted = strconv.AppendQuoteRune(f.intbuf[0:0], rune(c))
}
- f.padString(quoted)
+ f.pad(quoted)
}
// floating-point
return def
}
-// Add a plus sign or space to the floating-point string representation if missing and required.
-func (f *fmt) plusSpace(s string) {
- if s[0] != '-' {
+// formatFloat formats a float64; it is an efficient equivalent to f.pad(strconv.FormatFloat()...).
+func (f *fmt) formatFloat(v float64, verb byte, prec, n int) {
+ // We leave one byte at the beginning of f.intbuf for a sign if needed,
+ // and make it a space, which we might be able to use.
+ f.intbuf[0] = ' '
+ slice := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n)
+ // Add a plus sign or space to the floating-point string representation if missing and required.
+ // The formatted number starts at slice[1].
+ switch slice[1] {
+ case '-', '+':
+ // We're set; drop the leading space.
+ slice = slice[1:]
+ default:
+ // There's no sign, but we might need one.
if f.plus {
- s = "+" + s
+ slice[0] = '+'
} else if f.space {
- s = " " + s
+ // space is already there
+ } else {
+ slice = slice[1:]
}
}
- f.padString(s)
+ f.pad(slice)
}
// fmt_e64 formats a float64 in the form -1.23e+12.
-func (f *fmt) fmt_e64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'e', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_e64(v float64) { f.formatFloat(v, 'e', doPrec(f, 6), 64) }
// fmt_E64 formats a float64 in the form -1.23E+12.
-func (f *fmt) fmt_E64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'E', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_E64(v float64) { f.formatFloat(v, 'E', doPrec(f, 6), 64) }
// fmt_f64 formats a float64 in the form -1.23.
-func (f *fmt) fmt_f64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'f', doPrec(f, 6), 64)) }
+func (f *fmt) fmt_f64(v float64) { f.formatFloat(v, 'f', doPrec(f, 6), 64) }
// fmt_g64 formats a float64 in the 'f' or 'e' form according to size.
-func (f *fmt) fmt_g64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'g', doPrec(f, -1), 64)) }
+func (f *fmt) fmt_g64(v float64) { f.formatFloat(v, 'g', doPrec(f, -1), 64) }
// fmt_g64 formats a float64 in the 'f' or 'E' form according to size.
-func (f *fmt) fmt_G64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'G', doPrec(f, -1), 64)) }
+func (f *fmt) fmt_G64(v float64) { f.formatFloat(v, 'G', doPrec(f, -1), 64) }
// fmt_fb64 formats a float64 in the form -123p3 (exponent is power of 2).
-func (f *fmt) fmt_fb64(v float64) { f.plusSpace(strconv.FormatFloat(v, 'b', 0, 64)) }
+func (f *fmt) fmt_fb64(v float64) { f.formatFloat(v, 'b', 0, 64) }
// float32
// cannot defer to float64 versions
// because it will get rounding wrong in corner cases.
// fmt_e32 formats a float32 in the form -1.23e+12.
-func (f *fmt) fmt_e32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'e', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_e32(v float32) { f.formatFloat(float64(v), 'e', doPrec(f, 6), 32) }
// fmt_E32 formats a float32 in the form -1.23E+12.
-func (f *fmt) fmt_E32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'E', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_E32(v float32) { f.formatFloat(float64(v), 'E', doPrec(f, 6), 32) }
// fmt_f32 formats a float32 in the form -1.23.
-func (f *fmt) fmt_f32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'f', doPrec(f, 6), 32)) }
+func (f *fmt) fmt_f32(v float32) { f.formatFloat(float64(v), 'f', doPrec(f, 6), 32) }
// fmt_g32 formats a float32 in the 'f' or 'e' form according to size.
-func (f *fmt) fmt_g32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'g', doPrec(f, -1), 32)) }
+func (f *fmt) fmt_g32(v float32) { f.formatFloat(float64(v), 'g', doPrec(f, -1), 32) }
// fmt_G32 formats a float32 in the 'f' or 'E' form according to size.
-func (f *fmt) fmt_G32(v float32) { f.plusSpace(strconv.FormatFloat(float64(v), 'G', doPrec(f, -1), 32)) }
+func (f *fmt) fmt_G32(v float32) { f.formatFloat(float64(v), 'G', doPrec(f, -1), 32) }
// fmt_fb32 formats a float32 in the form -123p3 (exponent is power of 2).
-func (f *fmt) fmt_fb32(v float32) { f.padString(strconv.FormatFloat(float64(v), 'b', 0, 32)) }
+func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
// fmt_c64 formats a complex64 according to the verb.
func (f *fmt) fmt_c64(v complex64, verb rune) {
case 's':
p.fmt.fmt_s(v)
case 'x':
- p.fmt.fmt_sx(v)
+ p.fmt.fmt_sx(v, ldigits)
case 'X':
- p.fmt.fmt_sX(v)
+ p.fmt.fmt_sx(v, udigits)
case 'q':
p.fmt.fmt_q(v)
default:
case 's':
p.fmt.fmt_s(s)
case 'x':
- p.fmt.fmt_sx(s)
+ p.fmt.fmt_sx(s, ldigits)
case 'X':
- p.fmt.fmt_sX(s)
+ p.fmt.fmt_sx(s, udigits)
case 'q':
p.fmt.fmt_q(s)
default:
type Object struct {
Kind ObjKind
Name string // declared name
- Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil
+ Decl interface{} // corresponding Field, XxxSpec, FuncDecl, LabeledStmt, or AssignStmt; or nil
Data interface{} // object-specific data; or nil
Type interface{} // place holder for type information; may be nil
}
if d.Label.Name == name {
return d.Label.Pos()
}
+ case *AssignStmt:
+ for _, x := range d.Lhs {
+ if ident, isIdent := x.(*Ident); isIdent && ident.Name == name {
+ return ident.Pos()
+ }
+ }
}
return token.NoPos
}
{
"go/build/cgotest",
&DirInfo{
- CgoFiles: []string{"cgotest.go"},
+ CgoFiles: ifCgo([]string{"cgotest.go"}),
CFiles: []string{"cgotest.c"},
+ HFiles: []string{"cgotest.h"},
Imports: []string{"C", "unsafe"},
TestImports: []string{},
Package: "cgotest",
},
}
+func ifCgo(x []string) []string {
+ if DefaultContext.CgoEnabled {
+ return x
+ }
+ return nil
+}
+
const cmdtestOutput = "3"
func TestBuild(t *testing.T) {
continue
}
+ if tt.dir == "go/build/cgotest" && len(info.CgoFiles) == 0 {
+ continue
+ }
+
s, err := Build(tree, tt.dir, info)
if err != nil {
t.Errorf("Build(%#q): %v", tt.dir, err)
// A Context specifies the supporting context for a build.
type Context struct {
- GOARCH string // target architecture
- GOOS string // target operating system
- // TODO(rsc,adg): GOPATH
+ GOARCH string // target architecture
+ GOOS string // target operating system
+ CgoEnabled bool // whether cgo can be used
// By default, ScanDir uses the operating system's
// file system calls to read directories and files.
// The DefaultContext is the default Context for builds.
// It uses the GOARCH and GOOS environment variables
// if set, or else the compiled code's GOARCH and GOOS.
-var DefaultContext = Context{
- GOARCH: envOr("GOARCH", runtime.GOARCH),
- GOOS: envOr("GOOS", runtime.GOOS),
+var DefaultContext = defaultContext()
+
+var cgoEnabled = map[string]bool{
+ "darwin/386": true,
+ "darwin/amd64": true,
+ "linux/386": true,
+ "linux/amd64": true,
+ "freebsd/386": true,
+ "freebsd/amd64": true,
+ "windows/386": true,
+ "windows/amd64": true,
+}
+
+func defaultContext() Context {
+ var c Context
+
+ c.GOARCH = envOr("GOARCH", runtime.GOARCH)
+ c.GOOS = envOr("GOOS", runtime.GOOS)
+
+ s := os.Getenv("CGO_ENABLED")
+ switch s {
+ case "1":
+ c.CgoEnabled = true
+ case "0":
+ c.CgoEnabled = false
+ default:
+ c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+ }
+
+ return c
}
func envOr(name, def string) string {
// Source files
GoFiles []string // .go files in dir (excluding CgoFiles)
+ HFiles []string // .h files in dir
CFiles []string // .c files in dir
- SFiles []string // .s files in dir
+ SFiles []string // .s (and, when using cgo, .S files in dir)
CgoFiles []string // .go files that import "C"
// Cgo directives
return nil, err
}
+ var Sfiles []string // files with ".S" (capital S)
var di DirInfo
imported := make(map[string]bool)
testImported := make(map[string]bool)
ext := path.Ext(name)
switch ext {
- case ".go", ".c", ".s":
+ case ".go", ".c", ".s", ".h", ".S":
// tentatively okay
default:
// skip
case ".c":
di.CFiles = append(di.CFiles, name)
continue
+ case ".h":
+ di.HFiles = append(di.HFiles, name)
+ continue
case ".s":
di.SFiles = append(di.SFiles, name)
continue
+ case ".S":
+ Sfiles = append(Sfiles, name)
+ continue
}
pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
}
}
if isCgo {
- di.CgoFiles = append(di.CgoFiles, name)
+ if ctxt.CgoEnabled {
+ di.CgoFiles = append(di.CgoFiles, name)
+ }
} else if isTest {
if pkg == string(pf.Name.Name) {
di.TestGoFiles = append(di.TestGoFiles, name)
di.TestImports[i] = p
i++
}
+
+ // add the .S files only if we are using cgo
+ // (which means gcc will compile them).
+ // The standard assemblers expect .s files.
+ if len(di.CgoFiles) > 0 {
+ di.SFiles = append(di.SFiles, Sfiles...)
+ sort.Strings(di.SFiles)
+ }
+
// File name lists are sorted because ReadDir sorts.
sort.Strings(di.Imports)
sort.Strings(di.TestImports)
}
var slashslash = []byte("//")
-var plusBuild = []byte("+build")
// shouldBuild reports whether it is okay to use this file,
// The rule is that in the file's leading run of // comments
//
// $GOOS
// $GOARCH
-// $GOOS/$GOARCH
+// cgo (if cgo is enabled)
+// nocgo (if cgo is disabled)
+// a slash-separated list of any of these
//
func (ctxt *Context) matchOSArch(name string) bool {
+ if ctxt.CgoEnabled && name == "cgo" {
+ return true
+ }
+ if !ctxt.CgoEnabled && name == "nocgo" {
+ return true
+ }
if name == ctxt.GOOS || name == ctxt.GOARCH {
return true
}
i := strings.Index(name, "/")
- return i >= 0 && name[:i] == ctxt.GOOS && name[i+1:] == ctxt.GOARCH
+ return i >= 0 && ctxt.matchOSArch(name[:i]) && ctxt.matchOSArch(name[i+1:])
}
// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
func (t *Tree) BinDir() string {
if t.Goroot {
if gobin := os.Getenv("GOBIN"); gobin != "" {
- return gobin
+ return filepath.Clean(gobin)
}
}
return filepath.Join(t.Path, "bin")
}
var (
- ErrNotFound = errors.New("go/build: package could not be found locally")
- ErrTreeNotFound = errors.New("go/build: no valid GOROOT or GOPATH could be found")
+ ErrNotFound = errors.New("package could not be found locally")
+ ErrTreeNotFound = errors.New("no valid GOROOT or GOPATH could be found")
)
// FindTree takes an import or filesystem path and returns the
root := runtime.GOROOT()
t, err := newTree(root)
if err != nil {
- log.Printf("go/build: invalid GOROOT %q: %v", root, err)
+ log.Printf("invalid GOROOT %q: %v", root, err)
} else {
t.Goroot = true
Path = []*Tree{t}
}
t, err := newTree(p)
if err != nil {
- log.Printf("go/build: invalid GOPATH %q: %v", p, err)
+ log.Printf("invalid GOPATH %q: %v", p, err)
continue
}
Path = append(Path, t)
)
// ----------------------------------------------------------------------------
+// Collection of documentation info
-type typeDoc struct {
+// embeddedType describes the type of an anonymous field.
+//
+type embeddedType struct {
+ typ *typeInfo // the corresponding base type
+ ptr bool // if set, the anonymous field type is a pointer
+}
+
+type typeInfo struct {
// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
// if the type declaration hasn't been seen yet, decl is nil
- decl *ast.GenDecl
- // values, factory functions, and methods associated with the type
+ decl *ast.GenDecl
+ embedded []embeddedType
+ forward *TypeDoc // forward link to processed type documentation
+
+ // declarations associated with the type
values []*ast.GenDecl // consts and vars
factories map[string]*ast.FuncDecl
methods map[string]*ast.FuncDecl
}
+func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) {
+ info.embedded = append(info.embedded, embeddedType{embedded, isPtr})
+}
+
// docReader accumulates documentation for a single package.
// It modifies the AST: Comments (declaration documentation)
// that have been collected by the DocReader are set to nil
// printing the corresponding AST node).
//
type docReader struct {
- doc *ast.CommentGroup // package documentation, if any
- pkgName string
- values []*ast.GenDecl // consts and vars
- types map[string]*typeDoc
- funcs map[string]*ast.FuncDecl
- bugs []*ast.CommentGroup
+ doc *ast.CommentGroup // package documentation, if any
+ pkgName string
+ values []*ast.GenDecl // consts and vars
+ types map[string]*typeInfo
+ embedded map[string]*typeInfo // embedded types, possibly not exported
+ funcs map[string]*ast.FuncDecl
+ bugs []*ast.CommentGroup
}
func (doc *docReader) init(pkgName string) {
doc.pkgName = pkgName
- doc.types = make(map[string]*typeDoc)
+ doc.types = make(map[string]*typeInfo)
+ doc.embedded = make(map[string]*typeInfo)
doc.funcs = make(map[string]*ast.FuncDecl)
}
doc.doc = comments
return
}
-
// More than one package comment: Usually there will be only
// one file with a package comment, but it's better to collect
// all comments than drop them on the floor.
- // (This code isn't particularly clever - no amortized doubling is
- // used - but this situation occurs rarely and is not time-critical.)
- n1 := len(doc.doc.List)
- n2 := len(comments.List)
- list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
- copy(list, doc.doc.List)
- list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
- copy(list[n1+1:], comments.List)
- doc.doc = &ast.CommentGroup{list}
-}
-
-func (doc *docReader) addType(decl *ast.GenDecl) {
- spec := decl.Specs[0].(*ast.TypeSpec)
- typ := doc.lookupTypeDoc(spec.Name.Name)
- // typ should always be != nil since declared types
- // are always named - be conservative and check
- if typ != nil {
- // a type should be added at most once, so typ.decl
- // should be nil - if it isn't, simply overwrite it
- typ.decl = decl
- }
+ blankComment := &ast.Comment{token.NoPos, "//"}
+ list := append(doc.doc.List, blankComment)
+ doc.doc.List = append(list, comments.List...)
}
-func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
- if name == "" {
+func (doc *docReader) lookupTypeInfo(name string) *typeInfo {
+ if name == "" || name == "_" {
return nil // no type docs for anonymous types
}
- if tdoc, found := doc.types[name]; found {
- return tdoc
+ if info, found := doc.types[name]; found {
+ return info
}
// type wasn't found - add one without declaration
- tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl)}
- doc.types[name] = tdoc
- return tdoc
+ info := &typeInfo{
+ factories: make(map[string]*ast.FuncDecl),
+ methods: make(map[string]*ast.FuncDecl),
+ }
+ doc.types[name] = info
+ return info
}
-func baseTypeName(typ ast.Expr) string {
+func baseTypeName(typ ast.Expr, allTypes bool) string {
switch t := typ.(type) {
case *ast.Ident:
// if the type is not exported, the effect to
// a client is as if there were no type name
- if t.IsExported() {
+ if t.IsExported() || allTypes {
return t.Name
}
case *ast.StarExpr:
- return baseTypeName(t.X)
+ return baseTypeName(t.X, allTypes)
}
return ""
}
switch {
case v.Type != nil:
// a type is present; determine its name
- name = baseTypeName(v.Type)
+ name = baseTypeName(v.Type, false)
case decl.Tok == token.CONST:
// no type is present but we have a constant declaration;
// use the previous type name (w/o more type information
values := &doc.values
if domName != "" && domFreq >= int(float64(len(decl.Specs))*threshold) {
// typed entries are sufficiently frequent
- typ := doc.lookupTypeDoc(domName)
+ typ := doc.lookupTypeInfo(domName)
if typ != nil {
values = &typ.values // associate with that type
}
}
func (doc *docReader) addFunc(fun *ast.FuncDecl) {
+ // strip function body
+ fun.Body = nil
+
// determine if it should be associated with a type
if fun.Recv != nil {
// method
- typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type))
+ typ := doc.lookupTypeInfo(baseTypeName(fun.Recv.List[0].Type, false))
if typ != nil {
// exported receiver type
setFunc(typ.methods, fun)
// exactly one (named or anonymous) result associated
// with the first type in result signature (there may
// be more than one result)
- tname := baseTypeName(res.Type)
- typ := doc.lookupTypeDoc(tname)
+ tname := baseTypeName(res.Type, false)
+ typ := doc.lookupTypeInfo(tname)
if typ != nil {
// named and exported result type
setFunc(typ.factories, fun)
case token.TYPE:
// types are handled individually
for _, spec := range d.Specs {
- // make a (fake) GenDecl node for this TypeSpec
+ tspec := spec.(*ast.TypeSpec)
+ // add the type to the documentation
+ info := doc.lookupTypeInfo(tspec.Name.Name)
+ if info == nil {
+ continue // no name - ignore the type
+ }
+ // Make a (fake) GenDecl node for this TypeSpec
// (we need to do this here - as opposed to just
// for printing - so we don't lose the GenDecl
- // documentation)
+ // documentation). Since a new GenDecl node is
+ // created, there's no need to nil out d.Doc.
//
// TODO(gri): Consider just collecting the TypeSpec
// node (and copy in the GenDecl.doc if there is no
// makeTypeDocs below). Simpler data structures, but
// would lose GenDecl documentation if the TypeSpec
// has documentation as well.
- doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos})
- // A new GenDecl node is created, no need to nil out d.Doc.
+ fake := &ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos,
+ []ast.Spec{tspec}, token.NoPos}
+ // A type should be added at most once, so info.decl
+ // should be nil - if it isn't, simply overwrite it.
+ info.decl = fake
+ // Look for anonymous fields that might contribute methods.
+ var fields *ast.FieldList
+ switch typ := spec.(*ast.TypeSpec).Type.(type) {
+ case *ast.StructType:
+ fields = typ.Fields
+ case *ast.InterfaceType:
+ fields = typ.Methods
+ }
+ if fields != nil {
+ for _, field := range fields.List {
+ if len(field.Names) == 0 {
+ // anonymous field - add corresponding type
+ // to the info and collect it in doc
+ name := baseTypeName(field.Type, true)
+ if embedded := doc.lookupTypeInfo(name); embedded != nil {
+ _, ptr := field.Type.(*ast.StarExpr)
+ info.addEmbeddedType(embedded, ptr)
+ }
+ }
+ }
+ }
}
}
}
src.Comments = nil // consumed unassociated comments - remove from ast.File node
}
-func NewFileDoc(file *ast.File) *PackageDoc {
- var r docReader
- r.init(file.Name.Name)
- r.addFile(file)
- return r.newDoc("", nil)
-}
-
-func NewPackageDoc(pkg *ast.Package, importpath string) *PackageDoc {
+func NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDoc {
var r docReader
r.init(pkg.Name)
filenames := make([]string, len(pkg.Files))
i := 0
for filename, f := range pkg.Files {
+ if exportsOnly {
+ r.fileExports(f)
+ }
r.addFile(f)
filenames[i] = filename
i++
return d
}
+type methodSet map[string]*FuncDoc
+
+func (mset methodSet) add(m *FuncDoc) {
+ if mset[m.Name] == nil {
+ mset[m.Name] = m
+ }
+}
+
+func (mset methodSet) sortedList() []*FuncDoc {
+ list := make([]*FuncDoc, len(mset))
+ i := 0
+ for _, m := range mset {
+ list[i] = m
+ i++
+ }
+ sort.Sort(sortFuncDoc(list))
+ return list
+}
+
// TypeDoc is the documentation for a declared type.
// Consts and Vars are sorted lists of constants and variables of (mostly) that type.
// Factories is a sorted list of factory functions that return that type.
Consts []*ValueDoc
Vars []*ValueDoc
Factories []*FuncDoc
- Methods []*FuncDoc
+ methods []*FuncDoc // top-level methods only
+ embedded methodSet // embedded methods only
+ Methods []*FuncDoc // all methods including embedded ones
Decl *ast.GenDecl
order int
}
// NOTE(rsc): This would appear not to be correct for type ( )
// blocks, but the doc extractor above has split them into
// individual declarations.
-func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
- d := make([]*TypeDoc, len(m))
+func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {
+ // TODO(gri) Consider computing the embedded method information
+ // before calling makeTypeDocs. Then this function can
+ // be single-phased again. Also, it might simplify some
+ // of the logic.
+ //
+ // phase 1: associate collected declarations with TypeDocs
+ list := make([]*TypeDoc, len(m))
i := 0
for _, old := range m {
- // all typeDocs should have a declaration associated with
+ // all typeInfos should have a declaration associated with
// them after processing an entire package - be conservative
// and check
if decl := old.decl; decl != nil {
t.Consts = makeValueDocs(old.values, token.CONST)
t.Vars = makeValueDocs(old.values, token.VAR)
t.Factories = makeFuncDocs(old.factories)
- t.Methods = makeFuncDocs(old.methods)
+ t.methods = makeFuncDocs(old.methods)
+ // The list of embedded types' methods is computed from the list
+ // of embedded types, some of which may not have been processed
+ // yet (i.e., their forward link is nil) - do this in a 2nd phase.
+ // The final list of methods can only be computed after that -
+ // do this in a 3rd phase.
t.Decl = old.decl
t.order = i
- d[i] = t
+ old.forward = t // old has been processed
+ list[i] = t
i++
} else {
// no corresponding type declaration found - move any associated
}
}
}
- d = d[0:i] // some types may have been ignored
- sort.Sort(sortTypeDoc(d))
- return d
+ list = list[0:i] // some types may have been ignored
+
+ // phase 2: collect embedded methods for each processed typeInfo
+ for _, old := range m {
+ if t := old.forward; t != nil {
+ // old has been processed into t; collect embedded
+ // methods for t from the list of processed embedded
+ // types in old (and thus for which the methods are known)
+ typ := t.Type
+ if _, ok := typ.Type.(*ast.StructType); ok {
+ // struct
+ t.embedded = make(methodSet)
+ collectEmbeddedMethods(t.embedded, old, typ.Name.Name)
+ } else {
+ // interface
+ // TODO(gri) fix this
+ }
+ }
+ }
+
+ // phase 3: compute final method set for each TypeDoc
+ for _, d := range list {
+ if len(d.embedded) > 0 {
+ // there are embedded methods - exclude
+ // the ones with names conflicting with
+ // non-embedded methods
+ mset := make(methodSet)
+ // top-level methods have priority
+ for _, m := range d.methods {
+ mset.add(m)
+ }
+ // add non-conflicting embedded methods
+ for _, m := range d.embedded {
+ mset.add(m)
+ }
+ d.Methods = mset.sortedList()
+ } else {
+ // no embedded methods
+ d.Methods = d.methods
+ }
+ }
+
+ sort.Sort(sortTypeDoc(list))
+ return list
+}
+
+// collectEmbeddedMethods collects the embedded methods from all
+// processed embedded types found in info in mset. It considers
+// embedded types at the most shallow level first so that more
+// deeply nested embedded methods with conflicting names are
+// excluded.
+//
+func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string) {
+ for _, e := range info.embedded {
+ if e.typ.forward != nil { // == e was processed
+ for _, m := range e.typ.forward.methods {
+ mset.add(customizeRecv(m, e.ptr, recvTypeName))
+ }
+ collectEmbeddedMethods(mset, e.typ, recvTypeName)
+ }
+ }
+}
+
+func customizeRecv(m *FuncDoc, embeddedIsPtr bool, recvTypeName string) *FuncDoc {
+ if m == nil || m.Decl == nil || m.Decl.Recv == nil || len(m.Decl.Recv.List) != 1 {
+ return m // shouldn't happen, but be safe
+ }
+
+ // copy existing receiver field and set new type
+ // TODO(gri) is receiver type computation correct?
+ // what about deeply nested embeddings?
+ newField := *m.Decl.Recv.List[0]
+ _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
+ var typ ast.Expr = ast.NewIdent(recvTypeName)
+ if embeddedIsPtr || origRecvIsPtr {
+ typ = &ast.StarExpr{token.NoPos, typ}
+ }
+ newField.Type = typ
+
+ // copy existing receiver field list and set new receiver field
+ newFieldList := *m.Decl.Recv
+ newFieldList.List = []*ast.Field{&newField}
+
+ // copy existing function declaration and set new receiver field list
+ newFuncDecl := *m.Decl
+ newFuncDecl.Recv = &newFieldList
+
+ // copy existing function documentation and set new declaration
+ newM := *m
+ newM.Decl = &newFuncDecl
+ newM.Recv = typ
+
+ return &newM
}
func makeBugDocs(list []*ast.CommentGroup) []string {
p.Bugs = makeBugDocs(doc.bugs)
return p
}
-
-// ----------------------------------------------------------------------------
-// Filtering by name
-
-type Filter func(string) bool
-
-func matchFields(fields *ast.FieldList, f Filter) bool {
- if fields != nil {
- for _, field := range fields.List {
- for _, name := range field.Names {
- if f(name.Name) {
- return true
- }
- }
- }
- }
- return false
-}
-
-func matchDecl(d *ast.GenDecl, f Filter) bool {
- for _, d := range d.Specs {
- switch v := d.(type) {
- case *ast.ValueSpec:
- for _, name := range v.Names {
- if f(name.Name) {
- return true
- }
- }
- case *ast.TypeSpec:
- if f(v.Name.Name) {
- return true
- }
- switch t := v.Type.(type) {
- case *ast.StructType:
- if matchFields(t.Fields, f) {
- return true
- }
- case *ast.InterfaceType:
- if matchFields(t.Methods, f) {
- return true
- }
- }
- }
- }
- return false
-}
-
-func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
- w := 0
- for _, vd := range a {
- if matchDecl(vd.Decl, f) {
- a[w] = vd
- w++
- }
- }
- return a[0:w]
-}
-
-func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
- w := 0
- for _, fd := range a {
- if f(fd.Name) {
- a[w] = fd
- w++
- }
- }
- return a[0:w]
-}
-
-func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
- w := 0
- for _, td := range a {
- n := 0 // number of matches
- if matchDecl(td.Decl, f) {
- n = 1
- } else {
- // type name doesn't match, but we may have matching consts, vars, factories or methods
- td.Consts = filterValueDocs(td.Consts, f)
- td.Vars = filterValueDocs(td.Vars, f)
- td.Factories = filterFuncDocs(td.Factories, f)
- td.Methods = filterFuncDocs(td.Methods, f)
- n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods)
- }
- if n > 0 {
- a[w] = td
- w++
- }
- }
- return a[0:w]
-}
-
-// Filter eliminates documentation for names that don't pass through the filter f.
-// TODO: Recognize "Type.Method" as a name.
-//
-func (p *PackageDoc) Filter(f Filter) {
- p.Consts = filterValueDocs(p.Consts, f)
- p.Vars = filterValueDocs(p.Vars, f)
- p.Types = filterTypeDocs(p.Types, f)
- p.Funcs = filterFuncDocs(p.Funcs, f)
- p.Doc = "" // don't show top-level package doc
-}
--- /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.
+
+// This file implements export filtering of an AST.
+
+package doc
+
+import "go/ast"
+
+func filterIdentList(list []*ast.Ident) []*ast.Ident {
+ j := 0
+ for _, x := range list {
+ if ast.IsExported(x.Name) {
+ list[j] = x
+ j++
+ }
+ }
+ return list[0:j]
+}
+
+func baseName(x ast.Expr) *ast.Ident {
+ switch t := x.(type) {
+ case *ast.Ident:
+ return t
+ case *ast.SelectorExpr:
+ if _, ok := t.X.(*ast.Ident); ok {
+ return t.Sel
+ }
+ case *ast.StarExpr:
+ return baseName(t.X)
+ }
+ return nil
+}
+
+func (doc *docReader) filterFieldList(fields *ast.FieldList) (removedFields bool) {
+ if fields == nil {
+ return false
+ }
+ list := fields.List
+ j := 0
+ for _, f := range list {
+ keepField := false
+ if len(f.Names) == 0 {
+ // anonymous field
+ name := baseName(f.Type)
+ keepField = name != nil && name.IsExported()
+ } else {
+ n := len(f.Names)
+ f.Names = filterIdentList(f.Names)
+ if len(f.Names) < n {
+ removedFields = true
+ }
+ keepField = len(f.Names) > 0
+ }
+ if keepField {
+ doc.filterType(f.Type)
+ list[j] = f
+ j++
+ }
+ }
+ if j < len(list) {
+ removedFields = true
+ }
+ fields.List = list[0:j]
+ return
+}
+
+func (doc *docReader) filterParamList(fields *ast.FieldList) bool {
+ if fields == nil {
+ return false
+ }
+ var b bool
+ for _, f := range fields.List {
+ if doc.filterType(f.Type) {
+ b = true
+ }
+ }
+ return b
+}
+
+func (doc *docReader) filterType(typ ast.Expr) bool {
+ switch t := typ.(type) {
+ case *ast.Ident:
+ return ast.IsExported(t.Name)
+ case *ast.ParenExpr:
+ return doc.filterType(t.X)
+ case *ast.ArrayType:
+ return doc.filterType(t.Elt)
+ case *ast.StructType:
+ if doc.filterFieldList(t.Fields) {
+ t.Incomplete = true
+ }
+ return len(t.Fields.List) > 0
+ case *ast.FuncType:
+ b1 := doc.filterParamList(t.Params)
+ b2 := doc.filterParamList(t.Results)
+ return b1 || b2
+ case *ast.InterfaceType:
+ if doc.filterFieldList(t.Methods) {
+ t.Incomplete = true
+ }
+ return len(t.Methods.List) > 0
+ case *ast.MapType:
+ b1 := doc.filterType(t.Key)
+ b2 := doc.filterType(t.Value)
+ return b1 || b2
+ case *ast.ChanType:
+ return doc.filterType(t.Value)
+ }
+ return false
+}
+
+func (doc *docReader) filterSpec(spec ast.Spec) bool {
+ switch s := spec.(type) {
+ case *ast.ValueSpec:
+ s.Names = filterIdentList(s.Names)
+ if len(s.Names) > 0 {
+ doc.filterType(s.Type)
+ return true
+ }
+ case *ast.TypeSpec:
+ if ast.IsExported(s.Name.Name) {
+ doc.filterType(s.Type)
+ return true
+ }
+ }
+ return false
+}
+
+func (doc *docReader) filterSpecList(list []ast.Spec) []ast.Spec {
+ j := 0
+ for _, s := range list {
+ if doc.filterSpec(s) {
+ list[j] = s
+ j++
+ }
+ }
+ return list[0:j]
+}
+
+func (doc *docReader) filterDecl(decl ast.Decl) bool {
+ switch d := decl.(type) {
+ case *ast.GenDecl:
+ d.Specs = doc.filterSpecList(d.Specs)
+ return len(d.Specs) > 0
+ case *ast.FuncDecl:
+ return ast.IsExported(d.Name.Name)
+ }
+ return false
+}
+
+// fileExports trims the AST for a Go file in place such that
+// only exported nodes remain. fileExports returns true if
+// there are exported declarations; otherwise it returns false.
+//
+func (doc *docReader) fileExports(src *ast.File) bool {
+ j := 0
+ for _, d := range src.Decls {
+ if doc.filterDecl(d) {
+ src.Decls[j] = d
+ j++
+ }
+ }
+ src.Decls = src.Decls[0:j]
+ return j > 0
+}
--- /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 doc
+
+import "go/ast"
+
+type Filter func(string) bool
+
+func matchFields(fields *ast.FieldList, f Filter) bool {
+ if fields != nil {
+ for _, field := range fields.List {
+ for _, name := range field.Names {
+ if f(name.Name) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func matchDecl(d *ast.GenDecl, f Filter) bool {
+ for _, d := range d.Specs {
+ switch v := d.(type) {
+ case *ast.ValueSpec:
+ for _, name := range v.Names {
+ if f(name.Name) {
+ return true
+ }
+ }
+ case *ast.TypeSpec:
+ if f(v.Name.Name) {
+ return true
+ }
+ switch t := v.Type.(type) {
+ case *ast.StructType:
+ if matchFields(t.Fields, f) {
+ return true
+ }
+ case *ast.InterfaceType:
+ if matchFields(t.Methods, f) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
+ w := 0
+ for _, vd := range a {
+ if matchDecl(vd.Decl, f) {
+ a[w] = vd
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
+ w := 0
+ for _, fd := range a {
+ if f(fd.Name) {
+ a[w] = fd
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
+ w := 0
+ for _, td := range a {
+ n := 0 // number of matches
+ if matchDecl(td.Decl, f) {
+ n = 1
+ } else {
+ // type name doesn't match, but we may have matching consts, vars, factories or methods
+ td.Consts = filterValueDocs(td.Consts, f)
+ td.Vars = filterValueDocs(td.Vars, f)
+ td.Factories = filterFuncDocs(td.Factories, f)
+ td.Methods = filterFuncDocs(td.Methods, f)
+ n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods)
+ }
+ if n > 0 {
+ a[w] = td
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+// Filter eliminates documentation for names that don't pass through the filter f.
+// TODO: Recognize "Type.Method" as a name.
+//
+func (p *PackageDoc) Filter(f Filter) {
+ p.Consts = filterValueDocs(p.Consts, f)
+ p.Vars = filterValueDocs(p.Vars, f)
+ p.Types = filterTypeDocs(p.Types, f)
+ p.Funcs = filterFuncDocs(p.Funcs, f)
+ p.Doc = "" // don't show top-level package doc
+}
}
}
-func (p *parser) shortVarDecl(idents []*ast.Ident) {
+func (p *parser) shortVarDecl(decl *ast.AssignStmt, list []ast.Expr) {
// Go spec: A short variable declaration may redeclare variables
// provided they were originally declared in the same block with
// the same type, and at least one of the non-blank variables is new.
n := 0 // number of new variables
- for _, ident := range idents {
- assert(ident.Obj == nil, "identifier already declared or resolved")
- obj := ast.NewObj(ast.Var, ident.Name)
- // short var declarations cannot have redeclaration errors
- // and are not global => no need to remember the respective
- // declaration
- ident.Obj = obj
- if ident.Name != "_" {
- if alt := p.topScope.Insert(obj); alt != nil {
- ident.Obj = alt // redeclaration
- } else {
- n++ // new declaration
+ for _, x := range list {
+ if ident, isIdent := x.(*ast.Ident); isIdent {
+ assert(ident.Obj == nil, "identifier already declared or resolved")
+ obj := ast.NewObj(ast.Var, ident.Name)
+ // remember corresponding assignment for other tools
+ obj.Decl = decl
+ ident.Obj = obj
+ if ident.Name != "_" {
+ if alt := p.topScope.Insert(obj); alt != nil {
+ ident.Obj = alt // redeclaration
+ } else {
+ n++ // new declaration
+ }
}
+ } else {
+ p.errorExpected(x.Pos(), "identifier")
}
}
if n == 0 && p.mode&DeclarationErrors != 0 {
- p.error(idents[0].Pos(), "no new variables on left side of :=")
+ p.error(list[0].Pos(), "no new variables on left side of :=")
}
}
for i, x := range list {
ident, isIdent := x.(*ast.Ident)
if !isIdent {
- pos := x.(ast.Expr).Pos()
+ pos := x.Pos()
p.errorExpected(pos, "identifier")
ident = &ast.Ident{pos, "_", nil}
}
} else {
y = p.parseRhsList()
}
+ as := &ast.AssignStmt{x, pos, tok, y}
if tok == token.DEFINE {
- p.shortVarDecl(p.makeIdentList(x))
+ p.shortVarDecl(as, x)
}
- return &ast.AssignStmt{x, pos, tok, y}, isRange
+ return as, isRange
}
if len(x) > 1 {
comm = &ast.SendStmt{lhs[0], arrow, rhs}
} else {
// RecvStmt
- pos := p.pos
- tok := p.tok
- var rhs ast.Expr
- if tok == token.ASSIGN || tok == token.DEFINE {
+ if tok := p.tok; tok == token.ASSIGN || tok == token.DEFINE {
// RecvStmt with assignment
if len(lhs) > 2 {
p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
// continue with first two expressions
lhs = lhs[0:2]
}
+ pos := p.pos
p.next()
- rhs = p.parseRhs()
- if tok == token.DEFINE && lhs != nil {
- p.shortVarDecl(p.makeIdentList(lhs))
+ rhs := p.parseRhs()
+ as := &ast.AssignStmt{lhs, pos, tok, []ast.Expr{rhs}}
+ if tok == token.DEFINE {
+ p.shortVarDecl(as, lhs)
}
+ comm = as
} else {
- // rhs must be single receive operation
+ // lhs must be single receive operation
if len(lhs) > 1 {
p.errorExpected(lhs[0].Pos(), "1 expression")
// continue with first expression
}
- rhs = lhs[0]
- lhs = nil // there is no lhs
- }
- if lhs != nil {
- comm = &ast.AssignStmt{lhs, pos, tok, []ast.Expr{rhs}}
- } else {
- comm = &ast.ExprStmt{rhs}
+ comm = &ast.ExprStmt{lhs[0]}
}
}
} else {
// future (not yet interspersed) comments in this function.
//
func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
- n := p.nlines(line-p.pos.Line, min)
+ n := nlimit(line - p.pos.Line)
+ if n < min {
+ n = min
+ }
if n > 0 {
p.print(ws)
if newSection {
lbrace := fields.Opening
list := fields.List
rbrace := fields.Closing
+ hasComments := isIncomplete || p.commentBefore(p.fset.Position(rbrace))
srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line
- if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) && srcIsOneLine {
+ if !hasComments && srcIsOneLine {
// possibly a one-line struct/interface
if len(list) == 0 {
// no blank between keyword and {} in this case
return
}
}
+ // hasComments || !srcIsOneLine
+
+ p.print(blank, lbrace, token.LBRACE, indent)
+ if hasComments || len(list) > 0 {
+ p.print(formfeed)
+ }
- // at least one entry or incomplete
- p.print(blank, lbrace, token.LBRACE, indent, formfeed)
if isStruct {
sep := vtab
prev := tok
tok = declToken(d)
// if the declaration token changed (e.g., from CONST to TYPE)
+ // or the next declaration has documentation associated with it,
// print an empty line between top-level declarations
+ // (because p.linebreak is called with the position of d, which
+ // is past any documentation, the minimum requirement is satisfied
+ // even w/o the extra getDoc(d) nil-check - leave it in case the
+ // linebreak logic improves - there's already a TODO).
min := 1
- if prev != tok {
+ if prev != tok || getDoc(d) != nil {
min = 2
}
p.linebreak(p.fset.Position(d.Pos()).Line, min, ignore, false)
"text/tabwriter"
)
-const debug = false // enable for debugging
-const infinity = 1 << 30
+const (
+ maxNewlines = 2 // max. number of newlines between source text
+ debug = false // enable for debugging
+ infinity = 1 << 30
+)
type whiteSpace byte
}
}
-// nlines returns the adjusted number of linebreaks given the desired number
-// of breaks n such that min <= result <= max.
-//
-func (p *printer) nlines(n, min int) int {
- const max = 2 // max. number of newlines
- switch {
- case n < min:
- return min
- case n > max:
- return max
- }
- return n
-}
-
-// writeByte writes a single byte to p.output and updates p.pos.
+// writeByte writes ch to p.output and updates p.pos.
func (p *printer) writeByte(ch byte) {
p.output.WriteByte(ch)
p.pos.Offset++
}
}
-// writeNewlines writes up to n newlines to p.output and updates p.pos.
-// The actual number of newlines written is limited by nlines.
-// nl must be one of '\n' or '\f'.
-//
-func (p *printer) writeNewlines(n int, nl byte) {
- for n = p.nlines(n, 0); n > 0; n-- {
- p.writeByte(nl)
+// writeByteN writes ch n times to p.output and updates p.pos.
+func (p *printer) writeByteN(ch byte, n int) {
+ for n > 0 {
+ p.writeByte(ch)
+ n--
}
}
}
if pos.IsValid() && pos.Filename != p.last.Filename {
- // comment in a different file - separate with newlines (writeNewlines will limit the number)
- p.writeNewlines(10, '\f')
+ // comment in a different file - separate with newlines
+ p.writeByteN('\f', maxNewlines)
return
}
} else {
// comment on a different line:
// separate with at least one line break
+ droppedLinebreak := false
if prev == nil {
// first comment of a comment group
j := 0
case newline, formfeed:
// TODO(gri): may want to keep formfeed info in some cases
p.wsbuf[i] = ignore
+ droppedLinebreak = true
}
j = i
break
p.writeWhitespace(j)
}
- // turn off indent if we're about to print a line directive.
- indent := p.indent
- if strings.HasPrefix(comment.Text, linePrefix) {
- p.indent = 0
+ // determine number of linebreaks before the comment
+ n := 0
+ if pos.IsValid() && p.last.IsValid() {
+ n = pos.Line - p.last.Line
+ if n < 0 { // should never happen
+ n = 0
+ }
+ }
+
+ // at the package scope level only (p.indent == 0),
+ // add an extra newline if we dropped one before:
+ // this preserves a blank line before documentation
+ // comments at the package scope level (issue 2570)
+ if p.indent == 0 && droppedLinebreak {
+ n++
}
- // use formfeeds to break columns before a comment;
- // this is analogous to using formfeeds to separate
- // individual lines of /*-style comments - but make
- // sure there is at least one line break if the previous
- // comment was a line comment
- n := pos.Line - p.last.Line // if !pos.IsValid(), pos.Line == 0, and n will be 0
- if n <= 0 && prev != nil && prev.Text[1] == '/' {
+ // make sure there is at least one line break
+ // if the previous comment was a line comment
+ if n == 0 && prev != nil && prev.Text[1] == '/' {
n = 1
}
+
if n > 0 {
- p.writeNewlines(n, '\f')
+ // turn off indent if we're about to print a line directive
+ indent := p.indent
+ if strings.HasPrefix(comment.Text, linePrefix) {
+ p.indent = 0
+ }
+ // use formfeeds to break columns before a comment;
+ // this is analogous to using formfeeds to separate
+ // individual lines of /*-style comments
+ p.writeByteN('\f', nlimit(n))
+ p.indent = indent // restore indent
}
- p.indent = indent
}
}
// writeCommentSuffix writes a line break after a comment if indicated
// and processes any leftover indentation information. If a line break
// is needed, the kind of break (newline vs formfeed) depends on the
-// pending whitespace. writeCommentSuffix returns true if a pending
-// formfeed was dropped from the whitespace buffer.
+// pending whitespace. The writeCommentSuffix result indicates if a
+// newline was written or if a formfeed was dropped from the whitespace
+// buffer.
//
-func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
+func (p *printer) writeCommentSuffix(needsLinebreak bool) (wroteNewline, droppedFF bool) {
for i, ch := range p.wsbuf {
switch ch {
case blank, vtab:
// but remember if we dropped any formfeeds
if needsLinebreak {
needsLinebreak = false
+ wroteNewline = true
} else {
if ch == formfeed {
droppedFF = true
// make sure we have a line break
if needsLinebreak {
p.writeByte('\n')
+ wroteNewline = true
}
return
// intersperseComments consumes all comments that appear before the next token
// tok and prints it together with the buffered whitespace (i.e., the whitespace
// that needs to be written before the next token). A heuristic is used to mix
-// the comments and whitespace. intersperseComments returns true if a pending
-// formfeed was dropped from the whitespace buffer.
+// the comments and whitespace. The intersperseComments result indicates if a
+// newline was written or if a formfeed was dropped from the whitespace buffer.
//
-func (p *printer) intersperseComments(next token.Position, tok token.Token) (droppedFF bool) {
+func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
var last *ast.Comment
for ; p.commentBefore(next); p.cindex++ {
for _, c := range p.comments[p.cindex].List {
// no comment was written - we should never reach here since
// intersperseComments should not be called in that case
p.internalError("intersperseComments called without pending comments")
- return false
+ return
}
// whiteWhitespace writes the first n whitespace entries.
// ----------------------------------------------------------------------------
// Printing interface
+// nlines limits n to maxNewlines.
+func nlimit(n int) int {
+ if n > maxNewlines {
+ n = maxNewlines
+ }
+ return n
+}
+
func mayCombine(prev token.Token, next byte) (b bool) {
switch prev {
case token.INT:
p.pos = next
if data != "" {
- nl := byte('\n')
- if p.flush(next, tok) {
- nl = '\f' // dropped formfeed before
- }
+ wroteNewline, droppedFF := p.flush(next, tok)
// intersperse extra newlines if present in the source
// (don't do this in flush as it will cause extra newlines
- // at the end of a file) - use formfeeds if we dropped one
- // before
- if n := next.Line - p.pos.Line; n > 0 {
- p.writeNewlines(n, nl)
+ // at the end of a file)
+ n := nlimit(next.Line - p.pos.Line)
+ // don't exceed maxNewlines if we already wrote one
+ if wroteNewline && n == maxNewlines {
+ n = maxNewlines - 1
+ }
+ if n > 0 {
+ ch := byte('\n')
+ if droppedFF {
+ ch = '\f' // use formfeed since we dropped one before
+ }
+ p.writeByteN(ch, n)
}
p.writeItem(next, data, isLit)
return p.cindex < len(p.comments) && p.fset.Position(p.comments[p.cindex].List[0].Pos()).Offset < next.Offset
}
-// Flush prints any pending comments and whitespace occurring
-// textually before the position of the next token tok. Flush
-// returns true if a pending formfeed character was dropped
-// from the whitespace buffer as a result of interspersing
-// comments.
+// Flush prints any pending comments and whitespace occurring textually
+// before the position of the next token tok. The Flush result indicates
+// if a newline was written or if a formfeed was dropped from the whitespace
+// buffer.
//
-func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
+func (p *printer) flush(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
if p.commentBefore(next) {
// if there are comments before the next item, intersperse them
- droppedFF = p.intersperseComments(next, tok)
+ wroteNewline, droppedFF = p.intersperseComments(next, tok)
} else {
// otherwise, write any leftover whitespace
p.writeWhitespace(len(p.wsbuf))
// getNode returns the ast.CommentGroup associated with n, if any.
func getDoc(n ast.Node) *ast.CommentGroup {
switch n := n.(type) {
- // *ast.Fields cannot be printed separately - ignore for now
+ case *ast.Field:
+ return n.Doc
case *ast.ImportSpec:
return n.Doc
case *ast.ValueSpec:
var x int // x
var ()
-// This comment SHOULD be associated with the next declaration.
+// This comment SHOULD be associated with f0.
func f0() {
const pi = 3.14 // pi
var s1 struct{} /* an empty struct */ /* foo */
var s2 struct{} = struct{}{}
x := pi
}
+
//
-// NO SPACE HERE
+// This comment should be associated with f1, with one blank line before the comment.
//
func f1() {
f0()
var ()
-// This comment SHOULD be associated with the next declaration.
+// This comment SHOULD be associated with f0.
func f0() {
const pi = 3.14 // pi
var s1 struct {} /* an empty struct */ /* foo */
x := pi
}
//
-// NO SPACE HERE
+// This comment should be associated with f1, with one blank line before the comment.
//
func f1() {
f0()
func _() {
- // this comment should be properly indented
+// this comment should be properly indented
}
var _ int
+// at least one empty line between declarations of the same kind
+// if there is associated documentation (was issue 2570)
+type T1 struct{}
+
+// T2 comment
+type T2 struct {
+} // should be a two-line struct
+
+// T3 comment
+type T2 struct {
+} // should be a two-line struct
+
// printing of constant literals
const (
_ = "foobar"
}
}
+// no blank lines in empty structs and interfaces, but leave 1- or 2-line layout alone
+type _ struct{}
+type _ struct {
+}
+
+type _ interface{}
+type _ interface {
+}
+
// no tabs for single or ungrouped decls
func _() {
const xxxxxx = 0
import _ "io"
var _ int
+// at least one empty line between declarations of the same kind
+// if there is associated documentation (was issue 2570)
+type T1 struct{}
+// T2 comment
+type T2 struct {
+} // should be a two-line struct
+
+
+// T3 comment
+type T2 struct {
+
+
+} // should be a two-line struct
+
// printing of constant literals
const (
}
+// no blank lines in empty structs and interfaces, but leave 1- or 2-line layout alone
+type _ struct{ }
+type _ struct {
+
+}
+
+type _ interface{ }
+type _ interface {
+
+}
+
+
// no tabs for single or ungrouped decls
func _() {
const xxxxxx = 0
// Known bug: The first use call may have more than one empty line before
// (see go/printer/nodes.go, func linebreak).
-
use(x)
if x < x {
// Known bug: There may be more than one empty line before MoreCode()
// (see go/printer/nodes.go, func linebreak).
-
MoreCode()
}
S.next()
}
-func (S *Scanner) scanRawString() {
+func (S *Scanner) scanRawString() (hasCR bool) {
// '`' opening already consumed
offs := S.offset - 1
for S.ch != '`' {
ch := S.ch
S.next()
+ if ch == '\r' {
+ hasCR = true
+ }
if ch < 0 {
S.error(offs, "string not terminated")
break
}
S.next()
+ return
}
func (S *Scanner) skipWhitespace() {
return tok0
}
+func stripCR(b []byte) []byte {
+ c := make([]byte, len(b))
+ i := 0
+ for _, ch := range b {
+ if ch != '\r' {
+ c[i] = ch
+ i++
+ }
+ }
+ return c[:i]
+}
+
// Scan scans the next token and returns the token position,
// the token, and the literal string corresponding to the
// token. The source end is indicated by token.EOF.
insertSemi := false
offs := S.offset
tok := token.ILLEGAL
+ hasCR := false
// determine token value
switch ch := S.ch; {
case '`':
insertSemi = true
tok = token.STRING
- S.scanRawString()
+ hasCR = S.scanRawString()
case ':':
tok = S.switch2(token.COLON, token.DEFINE)
case '.':
// TODO(gri): The scanner API should change such that the literal string
// is only valid if an actual literal was scanned. This will
// permit a more efficient implementation.
- return S.file.Pos(offs), tok, string(S.src[offs:S.offset])
+ lit := S.src[offs:S.offset]
+ if hasCR {
+ lit = stripCR(lit)
+ }
+ return S.file.Pos(offs), tok, string(lit)
}
"`",
literal,
},
+ {token.STRING, "`\r`", literal},
+ {token.STRING, "`foo\r\nbar`", literal},
// Operators and delimiters
{token.ADD, "+", operator},
if tok != e.tok {
t.Errorf("bad token for %q: got %s, expected %s", lit, tok, e.tok)
}
- if e.tok.IsLiteral() && lit != e.lit {
- t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, e.lit)
+ if e.tok.IsLiteral() {
+ // no CRs in raw string literals
+ elit := e.lit
+ if elit[0] == '`' {
+ elit = string(stripCR([]byte(elit)))
+ epos.Offset += len(e.lit) - len(lit) // correct position
+ }
+ if lit != elit {
+ t.Errorf("bad literal for %q: got %q, expected %q", lit, lit, elit)
+ }
}
if tokenclass(tok) != e.class {
t.Errorf("bad class for %q: got %d, expected %d", lit, tokenclass(tok), e.class)
// Section 12.2.3.2 of the HTML5 specification says "The following elements
// have varying levels of special parsing rules".
// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
-var isSpecialElement = map[string]bool{
+var isSpecialElementMap = map[string]bool{
"address": true,
"applet": true,
"area": true,
"wbr": true,
"xmp": true,
}
+
+func isSpecialElement(element *Node) bool {
+ switch element.Namespace {
+ case "", "html":
+ return isSpecialElementMap[element.Data]
+ case "svg":
+ return element.Data == "foreignObject"
+ }
+ return false
+}
case "html":
p.im = beforeHeadIM
default:
- if p.top().Namespace == "" {
- continue
- }
- p.im = inForeignContentIM
+ continue
}
return
}
case "address", "div", "p":
continue
default:
- if !isSpecialElement[node.Data] {
+ if !isSpecialElement(node) {
continue
}
}
case "address", "div", "p":
continue
default:
- if !isSpecialElement[node.Data] {
+ if !isSpecialElement(node) {
continue
}
}
// TODO: adjust foreign attributes.
p.addElement(p.tok.Data, p.tok.Attr)
p.top().Namespace = namespace
- p.im = inForeignContentIM
return true
case "caption", "col", "colgroup", "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr":
// Ignore the token.
// Steps 5-6. Find the furthest block.
var furthestBlock *Node
for _, e := range p.oe[feIndex:] {
- if isSpecialElement[e.Data] {
+ if isSpecialElement(e) {
furthestBlock = e
break
}
p.oe = p.oe[:i]
break
}
- if isSpecialElement[p.oe[i].Data] {
+ if isSpecialElement(p.oe[i]) {
break
}
}
add = true
data = "tr"
consumed = false
+ case "caption", "col", "colgroup", "tbody", "tfoot", "thead":
+ if !p.popUntil(tableScopeStopTags, "tbody", "thead", "tfoot") {
+ // Ignore the token.
+ return true
+ }
+ p.im = inTableIM
+ return false
default:
// TODO.
}
Type: CommentNode,
Data: p.tok.Data,
})
+ case TextToken:
+ // Ignore all text but whitespace.
+ s := strings.Map(func(c rune) rune {
+ switch c {
+ case ' ', '\t', '\n', '\f', '\r':
+ return c
+ }
+ return -1
+ }, p.tok.Data)
+ if s != "" {
+ p.reconstructActiveFormattingElements()
+ p.addText(s)
+ }
case StartTagToken:
switch p.tok.Data {
case "html":
}
// Section 12.2.5.5.
-func inForeignContentIM(p *parser) bool {
+func parseForeignContent(p *parser) bool {
switch p.tok.Type {
+ case TextToken:
+ // TODO: HTML integration points.
+ if p.top().Namespace == "" {
+ inBodyIM(p)
+ p.resetInsertionMode()
+ return true
+ }
+ if p.framesetOK {
+ p.framesetOK = strings.TrimLeft(p.tok.Data, whitespace) == ""
+ }
+ p.addText(p.tok.Data)
case CommentToken:
p.addChild(&Node{
Type: CommentNode,
})
case StartTagToken:
if breakout[p.tok.Data] {
- // TODO.
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ // TODO: HTML, MathML integration points.
+ if p.oe[i].Namespace == "" {
+ p.oe = p.oe[:i+1]
+ break
+ }
+ }
+ return false
}
switch p.top().Namespace {
case "mathml":
// TODO: adjust foreign attributes.
p.addElement(p.tok.Data, p.tok.Attr)
case EndTagToken:
- // TODO.
+ for i := len(p.oe) - 1; i >= 0; i-- {
+ if p.oe[i].Namespace == "" {
+ return p.im(p)
+ }
+ if strings.EqualFold(p.oe[i].Data, p.tok.Data) {
+ p.oe = p.oe[:i]
+ break
+ }
+ }
+ return true
default:
// Ignore the token.
}
return true
}
+// Section 12.2.5.
+func (p *parser) inForeignContent() bool {
+ if len(p.oe) == 0 {
+ return false
+ }
+ n := p.oe[len(p.oe)-1]
+ if n.Namespace == "" {
+ return false
+ }
+ // TODO: MathML, HTML integration points.
+ // TODO: MathML's annotation-xml combining with SVG's svg.
+ return true
+}
+
func (p *parser) parse() error {
// Iterate until EOF. Any other error will cause an early return.
consumed := true
return err
}
}
- consumed = p.im(p)
+ if p.inForeignContent() {
+ consumed = parseForeignContent(p)
+ } else {
+ consumed = p.im(p)
+ }
}
// Loop until the final token (the ErrorToken signifying EOF) is consumed.
for {
{"tests3.dat", -1},
{"tests4.dat", -1},
{"tests5.dat", -1},
- {"tests6.dat", 36},
+ {"tests6.dat", 47},
+ {"tests10.dat", 16},
}
for _, tf := range testFiles {
f, err := os.Open("testdata/webkit/" + tf.filename)
func (e *Error) Error() string {
if e.Line != 0 {
- return fmt.Sprintf("exp/template/html:%s:%d: %s", e.Name, e.Line, e.Description)
+ return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description)
} else if e.Name != "" {
- return fmt.Sprintf("exp/template/html:%s: %s", e.Name, e.Description)
+ return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description)
}
- return "exp/template/html: " + e.Description
+ return "html/template: " + e.Description
}
// errorf creates an error given a format string f and args.
}
t := e.template(name)
if t == nil {
+ // Two cases: The template exists but is empty, or has never been mentioned at
+ // all. Distinguish the cases in the error messages.
+ if e.tmpl.set[name] != nil {
+ return context{
+ state: stateError,
+ err: errorf(ErrNoSuchTemplate, line, "%q is an incomplete or empty template", name),
+ }, dname
+ }
return context{
state: stateError,
- err: errorf(ErrNoSuchTemplate, line, "no such template %s", name),
+ err: errorf(ErrNoSuchTemplate, line, "no such template %q", name),
}, dname
}
if dname != name {
},
{
`{{template "foo"}}`,
- "z:1: no such template foo",
+ "z:1: no such template \"foo\"",
},
{
`<div{{template "y"}}>` +
},
{
`<input type=button value=onclick=>`,
- `exp/template/html:z: "=" in unquoted attr: "onclick="`,
+ `html/template:z: "=" in unquoted attr: "onclick="`,
},
{
`<input type=button value= onclick=>`,
- `exp/template/html:z: "=" in unquoted attr: "onclick="`,
+ `html/template:z: "=" in unquoted attr: "onclick="`,
},
{
`<input type=button value= 1+1=2>`,
- `exp/template/html:z: "=" in unquoted attr: "1+1=2"`,
+ `html/template:z: "=" in unquoted attr: "1+1=2"`,
},
{
"<a class=`foo>",
- "exp/template/html:z: \"`\" in unquoted attr: \"`foo\"",
+ "html/template:z: \"`\" in unquoted attr: \"`foo\"",
},
{
`<a style=font:'Arial'>`,
- `exp/template/html:z: "'" in unquoted attr: "font:'Arial'"`,
+ `html/template:z: "'" in unquoted attr: "font:'Arial'"`,
},
{
`<a=foo>`,
--- /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 color
+
+// RGBToYCbCr converts an RGB triple to a Y'CbCr triple. All components lie
+// within the range [0, 255].
+func RGBToYCbCr(r, g, b uint8) (uint8, uint8, uint8) {
+ // The JFIF specification says:
+ // Y' = 0.2990*R + 0.5870*G + 0.1140*B
+ // Cb = -0.1687*R - 0.3313*G + 0.5000*B + 128
+ // Cr = 0.5000*R - 0.4187*G - 0.0813*B + 128
+ // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
+ r1 := int(r)
+ g1 := int(g)
+ b1 := int(b)
+ yy := (19595*r1 + 38470*g1 + 7471*b1 + 1<<15) >> 16
+ cb := (-11056*r1 - 21712*g1 + 32768*b1 + 257<<15) >> 16
+ cr := (32768*r1 - 27440*g1 - 5328*b1 + 257<<15) >> 16
+ if yy < 0 {
+ yy = 0
+ } else if yy > 255 {
+ yy = 255
+ }
+ if cb < 0 {
+ cb = 0
+ } else if cb > 255 {
+ cb = 255
+ }
+ if cr < 0 {
+ cr = 0
+ } else if cr > 255 {
+ cr = 255
+ }
+ return uint8(yy), uint8(cb), uint8(cr)
+}
+
+// YCbCrToRGB converts a Y'CbCr triple to an RGB triple. All components lie
+// within the range [0, 255].
+func YCbCrToRGB(y, cb, cr uint8) (uint8, uint8, uint8) {
+ // The JFIF specification says:
+ // R = Y' + 1.40200*(Cr-128)
+ // G = Y' - 0.34414*(Cb-128) - 0.71414*(Cr-128)
+ // B = Y' + 1.77200*(Cb-128)
+ // http://www.w3.org/Graphics/JPEG/jfif3.pdf says Y but means Y'.
+ yy1 := int(y)<<16 + 1<<15
+ cb1 := int(cb) - 128
+ cr1 := int(cr) - 128
+ r := (yy1 + 91881*cr1) >> 16
+ g := (yy1 - 22554*cb1 - 46802*cr1) >> 16
+ b := (yy1 + 116130*cb1) >> 16
+ if r < 0 {
+ r = 0
+ } else if r > 255 {
+ r = 255
+ }
+ if g < 0 {
+ g = 0
+ } else if g > 255 {
+ g = 255
+ }
+ if b < 0 {
+ b = 0
+ } else if b > 255 {
+ b = 255
+ }
+ return uint8(r), uint8(g), uint8(b)
+}
+
+// YCbCr represents a fully opaque 24-bit Y'CbCr color, having 8 bits each for
+// one luma and two chroma components.
+//
+// JPEG, VP8, the MPEG family and other codecs use this color model. Such
+// codecs often use the terms YUV and Y'CbCr interchangeably, but strictly
+// speaking, the term YUV applies only to analog video signals, and Y' (luma)
+// is Y (luminance) after applying gamma correction.
+//
+// Conversion between RGB and Y'CbCr is lossy and there are multiple, slightly
+// different formulae for converting between the two. This package follows
+// the JFIF specification at http://www.w3.org/Graphics/JPEG/jfif3.pdf.
+type YCbCr struct {
+ Y, Cb, Cr uint8
+}
+
+func (c YCbCr) RGBA() (uint32, uint32, uint32, uint32) {
+ r, g, b := YCbCrToRGB(c.Y, c.Cb, c.Cr)
+ return uint32(r) * 0x101, uint32(g) * 0x101, uint32(b) * 0x101, 0xffff
+}
+
+// YCbCrModel is the Model for Y'CbCr colors.
+var YCbCrModel Model = ModelFunc(func(c Color) Color {
+ if _, ok := c.(YCbCr); ok {
+ return c
+ }
+ r, g, b, _ := c.RGBA()
+ y, u, v := RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+ return YCbCr{y, u, v}
+})
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package ycbcr
+package color
import (
"testing"
import (
"image"
"image/color"
- "image/ycbcr"
"testing"
)
}
dst = dst1
default:
- panic("unreachable")
+ b.Fatal("unknown destination color model", dcm)
}
var src image.Image
}
}
src = src1
- case ycbcr.YCbCrColorModel:
+ case color.YCbCrModel:
yy := make([]uint8, srcw*srch)
cb := make([]uint8, srcw*srch)
cr := make([]uint8, srcw*srch)
cb[i] = uint8(5 * i % 0x100)
cr[i] = uint8(7 * i % 0x100)
}
- src = &ycbcr.YCbCr{
+ src = &image.YCbCr{
Y: yy,
Cb: cb,
Cr: cr,
YStride: srcw,
CStride: srcw,
- SubsampleRatio: ycbcr.SubsampleRatio444,
+ SubsampleRatio: image.YCbCrSubsampleRatio444,
Rect: image.Rect(0, 0, srcw, srch),
}
default:
- panic("unreachable")
+ b.Fatal("unknown source color model", scm)
}
var mask image.Image
}
mask = mask1
default:
- panic("unreachable")
+ b.Fatal("unknown mask color model", mcm)
}
b.StartTimer()
}
func BenchmarkYCbCr(b *testing.B) {
- bench(b, color.RGBAModel, ycbcr.YCbCrColorModel, nil, Over)
+ bench(b, color.RGBAModel, color.YCbCrModel, nil, Over)
}
func BenchmarkGlyphOver(b *testing.B) {
import (
"image"
"image/color"
- "image/ycbcr"
)
// m is the maximum color value returned by image.Color.RGBA.
case *image.NRGBA:
drawNRGBAOver(dst0, r, src0, sp)
return
- case *ycbcr.YCbCr:
+ case *image.YCbCr:
drawYCbCr(dst0, r, src0, sp)
return
}
case *image.NRGBA:
drawNRGBASrc(dst0, r, src0, sp)
return
- case *ycbcr.YCbCr:
+ case *image.YCbCr:
drawYCbCr(dst0, r, src0, sp)
return
}
}
}
-func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *ycbcr.YCbCr, sp image.Point) {
- // A YCbCr image is always fully opaque, and so if the mask is implicitly nil
+func drawYCbCr(dst *image.RGBA, r image.Rectangle, src *image.YCbCr, sp image.Point) {
+ // An image.YCbCr is always fully opaque, and so if the mask is implicitly nil
// (i.e. fully opaque) then the op is effectively always Src.
var (
yy, cb, cr uint8
y0 := r.Min.Y - dst.Rect.Min.Y
y1 := r.Max.Y - dst.Rect.Min.Y
switch src.SubsampleRatio {
- case ycbcr.SubsampleRatio422:
+ case image.YCbCrSubsampleRatio422:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 {
yy = src.Y[sy*src.YStride+sx]
cb = src.Cb[sy*src.CStride+i]
cr = src.Cr[sy*src.CStride+i]
- rr, gg, bb := ycbcr.YCbCrToRGB(yy, cb, cr)
+ rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
dpix[x+3] = 255
}
}
- case ycbcr.SubsampleRatio420:
+ case image.YCbCrSubsampleRatio420:
for y, sy := y0, sp.Y; y != y1; y, sy = y+1, sy+1 {
dpix := dst.Pix[y*dst.Stride:]
for x, sx := x0, sp.X; x != x1; x, sx = x+4, sx+1 {
yy = src.Y[sy*src.YStride+sx]
cb = src.Cb[j*src.CStride+i]
cr = src.Cr[j*src.CStride+i]
- rr, gg, bb := ycbcr.YCbCrToRGB(yy, cb, cr)
+ rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
yy = src.Y[sy*src.YStride+sx]
cb = src.Cb[sy*src.CStride+sx]
cr = src.Cr[sy*src.CStride+sx]
- rr, gg, bb := ycbcr.YCbCrToRGB(yy, cb, cr)
+ rr, gg, bb := color.YCbCrToRGB(yy, cb, cr)
dpix[x+0] = rr
dpix[x+1] = gg
dpix[x+2] = bb
import (
"image"
"image/color"
- "image/ycbcr"
"testing"
)
}
func vgradCr() image.Image {
- m := &ycbcr.YCbCr{
+ m := &image.YCbCr{
Y: make([]byte, 16*16),
Cb: make([]byte, 16*16),
Cr: make([]byte, 16*16),
YStride: 16,
CStride: 16,
- SubsampleRatio: ycbcr.SubsampleRatio444,
+ SubsampleRatio: image.YCbCrSubsampleRatio444,
Rect: image.Rect(0, 0, 16, 16),
}
for y := 0; y < 16; y++ {
"bufio"
"image"
"image/color"
- "image/ycbcr"
"io"
)
r Reader
width, height int
img1 *image.Gray
- img3 *ycbcr.YCbCr
+ img3 *image.YCbCr
ri int // Restart Interval.
nComp int
comp [nColorComponent]component
d.img1 = m.SubImage(image.Rect(0, 0, d.width, d.height)).(*image.Gray)
return
}
- var subsampleRatio ycbcr.SubsampleRatio
+ var subsampleRatio image.YCbCrSubsampleRatio
n := h0 * v0
switch n {
case 1:
- subsampleRatio = ycbcr.SubsampleRatio444
+ subsampleRatio = image.YCbCrSubsampleRatio444
case 2:
- subsampleRatio = ycbcr.SubsampleRatio422
+ subsampleRatio = image.YCbCrSubsampleRatio422
case 4:
- subsampleRatio = ycbcr.SubsampleRatio420
+ subsampleRatio = image.YCbCrSubsampleRatio420
default:
panic("unreachable")
}
b := make([]byte, mxx*myy*(1*8*8*n+2*8*8))
- d.img3 = &ycbcr.YCbCr{
+ d.img3 = &image.YCbCr{
Y: b[mxx*myy*(0*8*8*n+0*8*8) : mxx*myy*(1*8*8*n+0*8*8)],
Cb: b[mxx*myy*(1*8*8*n+0*8*8) : mxx*myy*(1*8*8*n+1*8*8)],
Cr: b[mxx*myy*(1*8*8*n+1*8*8) : mxx*myy*(1*8*8*n+2*8*8)],
case nGrayComponent:
return image.Config{color.GrayModel, d.width, d.height}, nil
case nColorComponent:
- return image.Config{ycbcr.YCbCrColorModel, d.width, d.height}, nil
+ return image.Config{color.YCbCrModel, d.width, d.height}, nil
}
return image.Config{}, FormatError("missing SOF marker")
}
"bufio"
"errors"
"image"
- "image/ycbcr"
+ "image/color"
"io"
)
for j := 0; j < 8; j++ {
for i := 0; i < 8; i++ {
r, g, b, _ := m.At(min(p.X+i, xmax), min(p.Y+j, ymax)).RGBA()
- yy, cb, cr := ycbcr.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
+ yy, cb, cr := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
yBlock[8*j+i] = int(yy)
cbBlock[8*j+i] = int(cb)
crBlock[8*j+i] = int(cr)
sx = xmax
}
pix := m.Pix[offset+sx*4:]
- yy, cb, cr := ycbcr.RGBToYCbCr(pix[0], pix[1], pix[2])
+ yy, cb, cr := color.RGBToYCbCr(pix[0], pix[1], pix[2])
yBlock[8*j+i] = int(yy)
cbBlock[8*j+i] = int(cb)
crBlock[8*j+i] = int(cr)
}
}
if !img.Opaque() {
- panic("expected image to be opaque")
+ b.Fatal("expected image to be opaque")
}
b.SetBytes(640 * 480 * 4)
b.StartTimer()
}
}
if !img.Opaque() {
- panic("expected image to be opaque")
+ b.Fatal("expected image to be opaque")
}
b.SetBytes(640 * 480 * 4)
b.StartTimer()
b.StopTimer()
img := image.NewRGBA(image.Rect(0, 0, 640, 480))
if img.Opaque() {
- panic("expected image to not be opaque")
+ b.Fatal("expected image to not be opaque")
}
b.SetBytes(640 * 480 * 4)
b.StartTimer()
for i := 0; i < b.N; i++ {
_, err := Decode(r)
if err != nil {
- panic(err)
+ b.Fatal("Decode:", err)
}
}
}
--- /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 image
+
+import (
+ "image/color"
+)
+
+// YCbCrSubsampleRatio is the chroma subsample ratio used in a YCbCr image.
+type YCbCrSubsampleRatio int
+
+const (
+ YCbCrSubsampleRatio444 YCbCrSubsampleRatio = iota
+ YCbCrSubsampleRatio422
+ YCbCrSubsampleRatio420
+)
+
+// YCbCr is an in-memory image of Y'CbCr colors. There is one Y sample per
+// pixel, but each Cb and Cr sample can span one or more pixels.
+// YStride is the Y slice index delta between vertically adjacent pixels.
+// CStride is the Cb and Cr slice index delta between vertically adjacent pixels
+// that map to separate chroma samples.
+// It is not an absolute requirement, but YStride and len(Y) are typically
+// multiples of 8, and:
+// For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
+// For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
+// For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
+type YCbCr struct {
+ Y []uint8
+ Cb []uint8
+ Cr []uint8
+ YStride int
+ CStride int
+ SubsampleRatio YCbCrSubsampleRatio
+ Rect Rectangle
+}
+
+func (p *YCbCr) ColorModel() color.Model {
+ return color.YCbCrModel
+}
+
+func (p *YCbCr) Bounds() Rectangle {
+ return p.Rect
+}
+
+func (p *YCbCr) At(x, y int) color.Color {
+ if !(Point{x, y}.In(p.Rect)) {
+ return color.YCbCr{}
+ }
+ switch p.SubsampleRatio {
+ case YCbCrSubsampleRatio422:
+ i := x / 2
+ return color.YCbCr{
+ p.Y[y*p.YStride+x],
+ p.Cb[y*p.CStride+i],
+ p.Cr[y*p.CStride+i],
+ }
+ case YCbCrSubsampleRatio420:
+ i, j := x/2, y/2
+ return color.YCbCr{
+ p.Y[y*p.YStride+x],
+ p.Cb[j*p.CStride+i],
+ p.Cr[j*p.CStride+i],
+ }
+ }
+ // Default to 4:4:4 subsampling.
+ return color.YCbCr{
+ p.Y[y*p.YStride+x],
+ p.Cb[y*p.CStride+x],
+ p.Cr[y*p.CStride+x],
+ }
+}
+
+// SubImage returns an image representing the portion of the image p visible
+// through r. The returned value shares pixels with the original image.
+func (p *YCbCr) SubImage(r Rectangle) Image {
+ q := new(YCbCr)
+ *q = *p
+ q.Rect = q.Rect.Intersect(r)
+ return q
+}
+
+func (p *YCbCr) Opaque() bool {
+ return true
+}