libgo: Update to current version of master library.

From-SVN: r193688
This commit is contained in:
Ian Lance Taylor 2012-11-21 07:03:38 +00:00
parent a51fb17f48
commit fabcaa8df3
321 changed files with 62096 additions and 19248 deletions

View File

@ -1,4 +1,4 @@
291d9f1baf75 a070de932857
The first line of this file holds the Mercurial revision number of the The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources. last merge done from the master library sources.

View File

@ -773,7 +773,6 @@ go_net_files = \
go/net/lookup_unix.go \ go/net/lookup_unix.go \
go/net/mac.go \ go/net/mac.go \
go/net/net.go \ go/net/net.go \
go/net/net_posix.go \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
go/net/port.go \ go/net/port.go \
@ -1117,6 +1116,7 @@ go_crypto_x509_files = \
go/crypto/x509/pkcs8.go \ go/crypto/x509/pkcs8.go \
go/crypto/x509/root.go \ go/crypto/x509/root.go \
go/crypto/x509/root_unix.go \ go/crypto/x509/root_unix.go \
go/crypto/x509/sec1.go \
go/crypto/x509/verify.go \ go/crypto/x509/verify.go \
go/crypto/x509/x509.go go/crypto/x509/x509.go
@ -1245,10 +1245,17 @@ go_exp_terminal_files = \
go/exp/terminal/terminal.go \ go/exp/terminal/terminal.go \
go/exp/terminal/util.go go/exp/terminal/util.go
go_exp_types_files = \ go_exp_types_files = \
go/exp/types/builtins.go \
go/exp/types/check.go \ go/exp/types/check.go \
go/exp/types/const.go \ go/exp/types/const.go \
go/exp/types/conversions.go \
go/exp/types/errors.go \
go/exp/types/exportdata.go \ go/exp/types/exportdata.go \
go/exp/types/expr.go \
go/exp/types/gcimporter.go \ go/exp/types/gcimporter.go \
go/exp/types/operand.go \
go/exp/types/predicates.go \
go/exp/types/stmt.go \
go/exp/types/types.go \ go/exp/types/types.go \
go/exp/types/universe.go go/exp/types/universe.go
go_exp_utf8string_files = \ go_exp_utf8string_files = \
@ -1329,6 +1336,7 @@ go_image_jpeg_files = \
go/image/jpeg/huffman.go \ go/image/jpeg/huffman.go \
go/image/jpeg/idct.go \ go/image/jpeg/idct.go \
go/image/jpeg/reader.go \ go/image/jpeg/reader.go \
go/image/jpeg/scan.go \
go/image/jpeg/writer.go go/image/jpeg/writer.go
go_image_png_files = \ go_image_png_files = \

View File

@ -1027,7 +1027,6 @@ go_net_files = \
go/net/lookup_unix.go \ go/net/lookup_unix.go \
go/net/mac.go \ go/net/mac.go \
go/net/net.go \ go/net/net.go \
go/net/net_posix.go \
go/net/parse.go \ go/net/parse.go \
go/net/pipe.go \ go/net/pipe.go \
go/net/port.go \ go/net/port.go \
@ -1312,6 +1311,7 @@ go_crypto_x509_files = \
go/crypto/x509/pkcs8.go \ go/crypto/x509/pkcs8.go \
go/crypto/x509/root.go \ go/crypto/x509/root.go \
go/crypto/x509/root_unix.go \ go/crypto/x509/root_unix.go \
go/crypto/x509/sec1.go \
go/crypto/x509/verify.go \ go/crypto/x509/verify.go \
go/crypto/x509/x509.go go/crypto/x509/x509.go
@ -1463,10 +1463,17 @@ go_exp_terminal_files = \
go/exp/terminal/util.go go/exp/terminal/util.go
go_exp_types_files = \ go_exp_types_files = \
go/exp/types/builtins.go \
go/exp/types/check.go \ go/exp/types/check.go \
go/exp/types/const.go \ go/exp/types/const.go \
go/exp/types/conversions.go \
go/exp/types/errors.go \
go/exp/types/exportdata.go \ go/exp/types/exportdata.go \
go/exp/types/expr.go \
go/exp/types/gcimporter.go \ go/exp/types/gcimporter.go \
go/exp/types/operand.go \
go/exp/types/predicates.go \
go/exp/types/stmt.go \
go/exp/types/types.go \ go/exp/types/types.go \
go/exp/types/universe.go go/exp/types/universe.go
@ -1557,6 +1564,7 @@ go_image_jpeg_files = \
go/image/jpeg/huffman.go \ go/image/jpeg/huffman.go \
go/image/jpeg/idct.go \ go/image/jpeg/idct.go \
go/image/jpeg/reader.go \ go/image/jpeg/reader.go \
go/image/jpeg/scan.go \
go/image/jpeg/writer.go go/image/jpeg/writer.go
go_image_png_files = \ go_image_png_files = \

View File

@ -72,6 +72,18 @@ func cString(b []byte) string {
} }
func (tr *Reader) octal(b []byte) int64 { func (tr *Reader) octal(b []byte) int64 {
// Check for binary format first.
if len(b) > 0 && b[0]&0x80 != 0 {
var x int64
for i, c := range b {
if i == 0 {
c &= 0x7f // ignore signal bit in first byte
}
x = x<<8 | int64(c)
}
return x
}
// Removing leading spaces. // Removing leading spaces.
for len(b) > 0 && b[0] == ' ' { for len(b) > 0 && b[0] == ' ' {
b = b[1:] b = b[1:]

View File

@ -5,7 +5,10 @@
package tar package tar
import ( import (
"bytes"
"io/ioutil"
"os" "os"
"reflect"
"testing" "testing"
"time" "time"
) )
@ -54,3 +57,44 @@ func (symlink) Mode() os.FileMode { return os.ModeSymlink }
func (symlink) ModTime() time.Time { return time.Time{} } func (symlink) ModTime() time.Time { return time.Time{} }
func (symlink) IsDir() bool { return false } func (symlink) IsDir() bool { return false }
func (symlink) Sys() interface{} { return nil } func (symlink) Sys() interface{} { return nil }
func TestRoundTrip(t *testing.T) {
data := []byte("some file contents")
var b bytes.Buffer
tw := NewWriter(&b)
hdr := &Header{
Name: "file.txt",
Uid: 1 << 21, // too big for 8 octal digits
Size: int64(len(data)),
ModTime: time.Now(),
}
// tar only supports second precision.
hdr.ModTime = hdr.ModTime.Add(-time.Duration(hdr.ModTime.Nanosecond()) * time.Nanosecond)
if err := tw.WriteHeader(hdr); err != nil {
t.Fatalf("tw.WriteHeader: %v", err)
}
if _, err := tw.Write(data); err != nil {
t.Fatalf("tw.Write: %v", err)
}
if err := tw.Close(); err != nil {
t.Fatalf("tw.Close: %v", err)
}
// Read it back.
tr := NewReader(&b)
rHdr, err := tr.Next()
if err != nil {
t.Fatalf("tr.Next: %v", err)
}
if !reflect.DeepEqual(rHdr, hdr) {
t.Errorf("Header mismatch.\n got %+v\nwant %+v", rHdr, hdr)
}
rData, err := ioutil.ReadAll(tr)
if err != nil {
t.Fatalf("Read: %v", err)
}
if !bytes.Equal(rData, data) {
t.Errorf("Data mismatch.\n got %q\nwant %q", rData, data)
}
}

View File

@ -12,6 +12,7 @@ import (
"fmt" "fmt"
"io" "io"
"strconv" "strconv"
"time"
) )
var ( var (
@ -110,6 +111,12 @@ func (tw *Writer) numeric(b []byte, x int64) {
b[0] |= 0x80 // highest bit indicates binary format b[0] |= 0x80 // highest bit indicates binary format
} }
var (
minTime = time.Unix(0, 0)
// There is room for 11 octal digits (33 bits) of mtime.
maxTime = minTime.Add((1<<33 - 1) * time.Second)
)
// WriteHeader writes hdr and prepares to accept the file's contents. // WriteHeader writes hdr and prepares to accept the file's contents.
// WriteHeader calls Flush if it is not the first header. // WriteHeader calls Flush if it is not the first header.
// Calling after a Close will return ErrWriteAfterClose. // Calling after a Close will return ErrWriteAfterClose.
@ -133,19 +140,25 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
// TODO(dsymonds): handle names longer than 100 chars // TODO(dsymonds): handle names longer than 100 chars
copy(s.next(100), []byte(hdr.Name)) copy(s.next(100), []byte(hdr.Name))
tw.octal(s.next(8), hdr.Mode) // 100:108 // Handle out of range ModTime carefully.
tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116 var modTime int64
tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124 if !hdr.ModTime.Before(minTime) && !hdr.ModTime.After(maxTime) {
tw.numeric(s.next(12), hdr.Size) // 124:136 modTime = hdr.ModTime.Unix()
tw.numeric(s.next(12), hdr.ModTime.Unix()) // 136:148 }
s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157 tw.octal(s.next(8), hdr.Mode) // 100:108
tw.cString(s.next(100), hdr.Linkname) // linkname (157:257) tw.numeric(s.next(8), int64(hdr.Uid)) // 108:116
copy(s.next(8), []byte("ustar\x0000")) // 257:265 tw.numeric(s.next(8), int64(hdr.Gid)) // 116:124
tw.cString(s.next(32), hdr.Uname) // 265:297 tw.numeric(s.next(12), hdr.Size) // 124:136
tw.cString(s.next(32), hdr.Gname) // 297:329 tw.numeric(s.next(12), modTime) // 136:148
tw.numeric(s.next(8), hdr.Devmajor) // 329:337 s.next(8) // chksum (148:156)
tw.numeric(s.next(8), hdr.Devminor) // 337:345 s.next(1)[0] = hdr.Typeflag // 156:157
tw.cString(s.next(100), hdr.Linkname) // linkname (157:257)
copy(s.next(8), []byte("ustar\x0000")) // 257:265
tw.cString(s.next(32), hdr.Uname) // 265:297
tw.cString(s.next(32), hdr.Gname) // 297:329
tw.numeric(s.next(8), hdr.Devmajor) // 329:337
tw.numeric(s.next(8), hdr.Devminor) // 337:345
// Use the GNU magic instead of POSIX magic if we used any GNU extensions. // Use the GNU magic instead of POSIX magic if we used any GNU extensions.
if tw.usedBinary { if tw.usedBinary {

View File

@ -238,9 +238,12 @@ func readDirectoryHeader(f *File, r io.Reader) error {
if len(f.Extra) > 0 { if len(f.Extra) > 0 {
b := readBuf(f.Extra) b := readBuf(f.Extra)
for len(b) > 0 { for len(b) >= 4 { // need at least tag and size
tag := b.uint16() tag := b.uint16()
size := b.uint16() size := b.uint16()
if int(size) > len(b) {
return ErrFormat
}
if tag == zip64ExtraId { if tag == zip64ExtraId {
// update directory values from the zip64 extra block // update directory values from the zip64 extra block
eb := readBuf(b) eb := readBuf(b)
@ -256,6 +259,10 @@ func readDirectoryHeader(f *File, r io.Reader) error {
} }
b = b[size:] b = b[size:]
} }
// Should have consumed the whole header.
if len(b) != 0 {
return ErrFormat
}
} }
return nil return nil
} }

View File

@ -174,7 +174,7 @@ func (w *Writer) Create(name string) (io.Writer, error) {
} }
// CreateHeader adds a file to the zip file using the provided FileHeader // CreateHeader adds a file to the zip file using the provided FileHeader
// for the file metadata. // for the file metadata.
// It returns a Writer to which the file contents should be written. // It returns a Writer to which the file contents should be written.
// The file's contents must be written to the io.Writer before the next // The file's contents must be written to the io.Writer before the next
// call to Create, CreateHeader, or Close. // call to Create, CreateHeader, or Close.

View File

@ -173,3 +173,85 @@ func TestZip64(t *testing.T) {
t.Errorf("UncompressedSize64 %d, want %d", got, want) t.Errorf("UncompressedSize64 %d, want %d", got, want)
} }
} }
func testInvalidHeader(h *FileHeader, t *testing.T) {
var buf bytes.Buffer
z := NewWriter(&buf)
f, err := z.CreateHeader(h)
if err != nil {
t.Fatalf("error creating header: %v", err)
}
if _, err := f.Write([]byte("hi")); err != nil {
t.Fatalf("error writing content: %v", err)
}
if err := z.Close(); err != nil {
t.Fatalf("error closing zip writer: %v", err)
}
b := buf.Bytes()
if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != ErrFormat {
t.Fatalf("got %v, expected ErrFormat", err)
}
}
func testValidHeader(h *FileHeader, t *testing.T) {
var buf bytes.Buffer
z := NewWriter(&buf)
f, err := z.CreateHeader(h)
if err != nil {
t.Fatalf("error creating header: %v", err)
}
if _, err := f.Write([]byte("hi")); err != nil {
t.Fatalf("error writing content: %v", err)
}
if err := z.Close(); err != nil {
t.Fatalf("error closing zip writer: %v", err)
}
b := buf.Bytes()
if _, err = NewReader(bytes.NewReader(b), int64(len(b))); err != nil {
t.Fatalf("got %v, expected nil", err)
}
}
// Issue 4302.
func TestHeaderInvalidTagAndSize(t *testing.T) {
const timeFormat = "20060102T150405.000.txt"
ts := time.Now()
filename := ts.Format(timeFormat)
h := FileHeader{
Name: filename,
Method: Deflate,
Extra: []byte(ts.Format(time.RFC3339Nano)), // missing tag and len
}
h.SetModTime(ts)
testInvalidHeader(&h, t)
}
func TestHeaderTooShort(t *testing.T) {
h := FileHeader{
Name: "foo.txt",
Method: Deflate,
Extra: []byte{zip64ExtraId}, // missing size
}
testInvalidHeader(&h, t)
}
// Issue 4393. It is valid to have an extra data header
// which contains no body.
func TestZeroLengthHeader(t *testing.T) {
h := FileHeader{
Name: "extadata.txt",
Method: Deflate,
Extra: []byte{
85, 84, 5, 0, 3, 154, 144, 195, 77, // tag 21589 size 5
85, 120, 0, 0, // tag 30805 size 0
},
}
testValidHeader(&h, t)
}

View File

@ -567,6 +567,36 @@ func (b *Writer) WriteString(s string) (int, error) {
return nn, nil return nn, nil
} }
// ReadFrom implements io.ReaderFrom.
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
if b.Buffered() == 0 {
if w, ok := b.wr.(io.ReaderFrom); ok {
return w.ReadFrom(r)
}
}
var m int
for {
m, err = r.Read(b.buf[b.n:])
if m == 0 {
break
}
b.n += m
n += int64(m)
if b.Available() == 0 {
if err1 := b.Flush(); err1 != nil {
return n, err1
}
}
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
return n, err
}
// buffered input and output // buffered input and output
// ReadWriter stores pointers to a Reader and a Writer. // ReadWriter stores pointers to a Reader and a Writer.

View File

@ -763,8 +763,8 @@ func testReadLineNewlines(t *testing.T, input string, expect []readLineResult) {
} }
} }
func TestReaderWriteTo(t *testing.T) { func createTestInput(n int) []byte {
input := make([]byte, 8192) input := make([]byte, n)
for i := range input { for i := range input {
// 101 and 251 are arbitrary prime numbers. // 101 and 251 are arbitrary prime numbers.
// The idea is to create an input sequence // The idea is to create an input sequence
@ -774,7 +774,12 @@ func TestReaderWriteTo(t *testing.T) {
input[i] ^= byte(i / 101) input[i] ^= byte(i / 101)
} }
} }
r := NewReader(bytes.NewBuffer(input)) return input
}
func TestReaderWriteTo(t *testing.T) {
input := createTestInput(8192)
r := NewReader(onlyReader{bytes.NewBuffer(input)})
w := new(bytes.Buffer) w := new(bytes.Buffer)
if n, err := r.WriteTo(w); err != nil || n != int64(len(input)) { if n, err := r.WriteTo(w); err != nil || n != int64(len(input)) {
t.Fatalf("r.WriteTo(w) = %d, %v, want %d, nil", n, err, len(input)) t.Fatalf("r.WriteTo(w) = %d, %v, want %d, nil", n, err, len(input))
@ -817,12 +822,129 @@ func TestReaderWriteToErrors(t *testing.T) {
} }
} }
func TestWriterReadFrom(t *testing.T) {
ws := []func(io.Writer) io.Writer{
func(w io.Writer) io.Writer { return onlyWriter{w} },
func(w io.Writer) io.Writer { return w },
}
rs := []func(io.Reader) io.Reader{
iotest.DataErrReader,
func(r io.Reader) io.Reader { return r },
}
for ri, rfunc := range rs {
for wi, wfunc := range ws {
input := createTestInput(8192)
b := new(bytes.Buffer)
w := NewWriter(wfunc(b))
r := rfunc(bytes.NewBuffer(input))
if n, err := w.ReadFrom(r); err != nil || n != int64(len(input)) {
t.Errorf("ws[%d],rs[%d]: w.ReadFrom(r) = %d, %v, want %d, nil", wi, ri, n, err, len(input))
continue
}
if got, want := b.String(), string(input); got != want {
t.Errorf("ws[%d], rs[%d]:\ngot %q\nwant %q\n", wi, ri, got, want)
}
}
}
}
type errorReaderFromTest struct {
rn, wn int
rerr, werr error
expected error
}
func (r errorReaderFromTest) Read(p []byte) (int, error) {
return len(p) * r.rn, r.rerr
}
func (w errorReaderFromTest) Write(p []byte) (int, error) {
return len(p) * w.wn, w.werr
}
var errorReaderFromTests = []errorReaderFromTest{
{0, 1, io.EOF, nil, nil},
{1, 1, io.EOF, nil, nil},
{0, 1, io.ErrClosedPipe, nil, io.ErrClosedPipe},
{0, 0, io.ErrClosedPipe, io.ErrShortWrite, io.ErrClosedPipe},
{1, 0, nil, io.ErrShortWrite, io.ErrShortWrite},
}
func TestWriterReadFromErrors(t *testing.T) {
for i, rw := range errorReaderFromTests {
w := NewWriter(rw)
if _, err := w.ReadFrom(rw); err != rw.expected {
t.Errorf("w.ReadFrom(errorReaderFromTests[%d]) = _, %v, want _,%v", i, err, rw.expected)
}
}
}
// TestWriterReadFromCounts tests that using io.Copy to copy into a
// bufio.Writer does not prematurely flush the buffer. For example, when
// buffering writes to a network socket, excessive network writes should be
// avoided.
func TestWriterReadFromCounts(t *testing.T) {
var w0 writeCountingDiscard
b0 := NewWriterSize(&w0, 1234)
b0.WriteString(strings.Repeat("x", 1000))
if w0 != 0 {
t.Fatalf("write 1000 'x's: got %d writes, want 0", w0)
}
b0.WriteString(strings.Repeat("x", 200))
if w0 != 0 {
t.Fatalf("write 1200 'x's: got %d writes, want 0", w0)
}
io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 30))})
if w0 != 0 {
t.Fatalf("write 1230 'x's: got %d writes, want 0", w0)
}
io.Copy(b0, onlyReader{strings.NewReader(strings.Repeat("x", 9))})
if w0 != 1 {
t.Fatalf("write 1239 'x's: got %d writes, want 1", w0)
}
var w1 writeCountingDiscard
b1 := NewWriterSize(&w1, 1234)
b1.WriteString(strings.Repeat("x", 1200))
b1.Flush()
if w1 != 1 {
t.Fatalf("flush 1200 'x's: got %d writes, want 1", w1)
}
b1.WriteString(strings.Repeat("x", 89))
if w1 != 1 {
t.Fatalf("write 1200 + 89 'x's: got %d writes, want 1", w1)
}
io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 700))})
if w1 != 1 {
t.Fatalf("write 1200 + 789 'x's: got %d writes, want 1", w1)
}
io.Copy(b1, onlyReader{strings.NewReader(strings.Repeat("x", 600))})
if w1 != 2 {
t.Fatalf("write 1200 + 1389 'x's: got %d writes, want 2", w1)
}
b1.Flush()
if w1 != 3 {
t.Fatalf("flush 1200 + 1389 'x's: got %d writes, want 3", w1)
}
}
// A writeCountingDiscard is like ioutil.Discard and counts the number of times
// Write is called on it.
type writeCountingDiscard int
func (w *writeCountingDiscard) Write(p []byte) (int, error) {
*w++
return len(p), nil
}
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have. // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
type onlyReader struct { type onlyReader struct {
r io.Reader r io.Reader
} }
func (r *onlyReader) Read(b []byte) (int, error) { func (r onlyReader) Read(b []byte) (int, error) {
return r.r.Read(b) return r.r.Read(b)
} }
@ -831,7 +953,7 @@ type onlyWriter struct {
w io.Writer w io.Writer
} }
func (w *onlyWriter) Write(b []byte) (int, error) { func (w onlyWriter) Write(b []byte) (int, error) {
return w.w.Write(b) return w.w.Write(b)
} }
@ -840,7 +962,7 @@ func BenchmarkReaderCopyOptimal(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
src := NewReader(bytes.NewBuffer(make([]byte, 8192))) src := NewReader(bytes.NewBuffer(make([]byte, 8192)))
dst := &onlyWriter{new(bytes.Buffer)} dst := onlyWriter{new(bytes.Buffer)}
b.StartTimer() b.StartTimer()
io.Copy(dst, src) io.Copy(dst, src)
} }
@ -850,8 +972,8 @@ func BenchmarkReaderCopyUnoptimal(b *testing.B) {
// Unoptimal case is where the underlying reader doesn't implement io.WriterTo // Unoptimal case is where the underlying reader doesn't implement io.WriterTo
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
src := NewReader(&onlyReader{bytes.NewBuffer(make([]byte, 8192))}) src := NewReader(onlyReader{bytes.NewBuffer(make([]byte, 8192))})
dst := &onlyWriter{new(bytes.Buffer)} dst := onlyWriter{new(bytes.Buffer)}
b.StartTimer() b.StartTimer()
io.Copy(dst, src) io.Copy(dst, src)
} }
@ -860,8 +982,39 @@ func BenchmarkReaderCopyUnoptimal(b *testing.B) {
func BenchmarkReaderCopyNoWriteTo(b *testing.B) { func BenchmarkReaderCopyNoWriteTo(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
b.StopTimer() b.StopTimer()
src := &onlyReader{NewReader(bytes.NewBuffer(make([]byte, 8192)))} src := onlyReader{NewReader(bytes.NewBuffer(make([]byte, 8192)))}
dst := &onlyWriter{new(bytes.Buffer)} dst := onlyWriter{new(bytes.Buffer)}
b.StartTimer()
io.Copy(dst, src)
}
}
func BenchmarkWriterCopyOptimal(b *testing.B) {
// Optimal case is where the underlying writer implements io.ReaderFrom
for i := 0; i < b.N; i++ {
b.StopTimer()
src := onlyReader{bytes.NewBuffer(make([]byte, 8192))}
dst := NewWriter(new(bytes.Buffer))
b.StartTimer()
io.Copy(dst, src)
}
}
func BenchmarkWriterCopyUnoptimal(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
src := onlyReader{bytes.NewBuffer(make([]byte, 8192))}
dst := NewWriter(onlyWriter{new(bytes.Buffer)})
b.StartTimer()
io.Copy(dst, src)
}
}
func BenchmarkWriterCopyNoReadFrom(b *testing.B) {
for i := 0; i < b.N; i++ {
b.StopTimer()
src := onlyReader{bytes.NewBuffer(make([]byte, 8192))}
dst := onlyWriter{NewWriter(new(bytes.Buffer))}
b.StartTimer() b.StartTimer()
io.Copy(dst, src) io.Copy(dst, src)
} }

View File

@ -251,10 +251,10 @@ func TestReadFrom(t *testing.T) {
func TestWriteTo(t *testing.T) { func TestWriteTo(t *testing.T) {
var buf Buffer var buf Buffer
for i := 3; i < 30; i += 3 { for i := 3; i < 30; i += 3 {
s := fillBytes(t, "TestReadFrom (1)", &buf, "", 5, testBytes[0:len(testBytes)/i]) s := fillBytes(t, "TestWriteTo (1)", &buf, "", 5, testBytes[0:len(testBytes)/i])
var b Buffer var b Buffer
buf.WriteTo(&b) buf.WriteTo(&b)
empty(t, "TestReadFrom (2)", &b, s, make([]byte, len(data))) empty(t, "TestWriteTo (2)", &b, s, make([]byte, len(data)))
} }
} }

View File

@ -1026,7 +1026,7 @@ func TestEqualFold(t *testing.T) {
var makeFieldsInput = func() []byte { var makeFieldsInput = func() []byte {
x := make([]byte, 1<<20) x := make([]byte, 1<<20)
// Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space. // Input is ~10% space, ~10% 2-byte UTF-8, rest ASCII non-space.
for i := range x { for i := range x {
switch rand.Intn(10) { switch rand.Intn(10) {
case 0: case 0:

View File

@ -10,7 +10,7 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// A Reader implements the io.Reader, io.ReaderAt, io.Seeker, // A Reader implements the io.Reader, io.ReaderAt, io.WriterTo, io.Seeker,
// io.ByteScanner, and io.RuneScanner interfaces by reading from // io.ByteScanner, and io.RuneScanner interfaces by reading from
// a byte slice. // a byte slice.
// Unlike a Buffer, a Reader is read-only and supports seeking. // Unlike a Buffer, a Reader is read-only and supports seeking.
@ -121,5 +121,24 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
return abs, nil return abs, nil
} }
// WriteTo implements the io.WriterTo interface.
func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
r.prevRune = -1
if r.i >= len(r.s) {
return 0, io.EOF
}
b := r.s[r.i:]
m, err := w.Write(b)
if m > len(b) {
panic("bytes.Reader.WriteTo: invalid Write count")
}
r.i += m
n = int64(m)
if m != len(b) && err == nil {
err = io.ErrShortWrite
}
return
}
// NewReader returns a new Reader reading from b. // NewReader returns a new Reader reading from b.
func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} } func NewReader(b []byte) *Reader { return &Reader{b, 0, -1} }

View File

@ -86,3 +86,24 @@ func TestReaderAt(t *testing.T) {
} }
} }
} }
func TestReaderWriteTo(t *testing.T) {
for i := 3; i < 30; i += 3 {
s := data[:len(data)/i]
r := NewReader(testBytes[:len(testBytes)/i])
var b Buffer
n, err := r.WriteTo(&b)
if expect := int64(len(s)); n != expect {
t.Errorf("got %v; want %v", n, expect)
}
if err != nil {
t.Errorf("got error = %v; want nil", err)
}
if b.String() != s {
t.Errorf("got string %q; want %q", b.String(), s)
}
if r.Len() != 0 {
t.Errorf("reader contains %v bytes; want 0", r.Len())
}
}
}

View File

@ -14,21 +14,16 @@ import (
// because the error handling was verbose. Instead, any error is kept and can // because the error handling was verbose. Instead, any error is kept and can
// be checked afterwards. // be checked afterwards.
type bitReader struct { type bitReader struct {
r byteReader r io.ByteReader
n uint64 n uint64
bits uint bits uint
err error err error
} }
// bitReader needs to read bytes from an io.Reader. We attempt to convert the // newBitReader returns a new bitReader reading from r. If r is not
// given io.Reader to this interface and, if it doesn't already fit, we wrap in // already an io.ByteReader, it will be converted via a bufio.Reader.
// a bufio.Reader.
type byteReader interface {
ReadByte() (byte, error)
}
func newBitReader(r io.Reader) bitReader { func newBitReader(r io.Reader) bitReader {
byter, ok := r.(byteReader) byter, ok := r.(io.ByteReader)
if !ok { if !ok {
byter = bufio.NewReader(r) byter = bufio.NewReader(r)
} }

View File

@ -208,8 +208,8 @@ type decompressor struct {
h1, h2 huffmanDecoder h1, h2 huffmanDecoder
// Length arrays used to define Huffman codes. // Length arrays used to define Huffman codes.
bits [maxLit + maxDist]int bits *[maxLit + maxDist]int
codebits [numCodes]int codebits *[numCodes]int
// Output history, buffer. // Output history, buffer.
hist *[maxHist]byte hist *[maxHist]byte
@ -692,6 +692,8 @@ func makeReader(r io.Reader) Reader {
// finished reading. // finished reading.
func NewReader(r io.Reader) io.ReadCloser { func NewReader(r io.Reader) io.ReadCloser {
var f decompressor var f decompressor
f.bits = new([maxLit + maxDist]int)
f.codebits = new([numCodes]int)
f.r = makeReader(r) f.r = makeReader(r)
f.hist = new([maxHist]byte) f.hist = new([maxHist]byte)
f.step = (*decompressor).nextBlock f.step = (*decompressor).nextBlock
@ -707,6 +709,8 @@ func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
var f decompressor var f decompressor
f.r = makeReader(r) f.r = makeReader(r)
f.hist = new([maxHist]byte) f.hist = new([maxHist]byte)
f.bits = new([maxLit + maxDist]int)
f.codebits = new([numCodes]int)
f.step = (*decompressor).nextBlock f.step = (*decompressor).nextBlock
f.setDict(dict) f.setDict(dict)
return &f return &f

View File

@ -176,7 +176,7 @@ func (l *List) MoveToBack(e *Element) {
l.insert(l.remove(e), l.root.prev) l.insert(l.remove(e), l.root.prev)
} }
// PuchBackList inserts a copy of an other list at the back of list l. // PushBackList inserts a copy of an other list at the back of list l.
// The lists l and other may be the same. // The lists l and other may be the same.
func (l *List) PushBackList(other *List) { func (l *List) PushBackList(other *List) {
l.lazyInit() l.lazyInit()

View File

@ -33,6 +33,9 @@ type cbcEncrypter cbc
// mode, using the given Block. The length of iv must be the same as the // mode, using the given Block. The length of iv must be the same as the
// Block's block size. // Block's block size.
func NewCBCEncrypter(b Block, iv []byte) BlockMode { func NewCBCEncrypter(b Block, iv []byte) BlockMode {
if len(iv) != b.BlockSize() {
panic("cipher.NewCBCEncrypter: IV length must equal block size")
}
return (*cbcEncrypter)(newCBC(b, iv)) return (*cbcEncrypter)(newCBC(b, iv))
} }
@ -58,6 +61,9 @@ type cbcDecrypter cbc
// mode, using the given Block. The length of iv must be the same as the // mode, using the given Block. The length of iv must be the same as the
// Block's block size and must match the iv used to encrypt the data. // Block's block size and must match the iv used to encrypt the data.
func NewCBCDecrypter(b Block, iv []byte) BlockMode { func NewCBCDecrypter(b Block, iv []byte) BlockMode {
if len(iv) != b.BlockSize() {
panic("cipher.NewCBCDecrypter: IV length must equal block size")
}
return (*cbcDecrypter)(newCBC(b, iv)) return (*cbcDecrypter)(newCBC(b, iv))
} }

View File

@ -17,6 +17,9 @@ type cfb struct {
// using the given Block. The iv must be the same length as the Block's block // using the given Block. The iv must be the same length as the Block's block
// size. // size.
func NewCFBEncrypter(block Block, iv []byte) Stream { func NewCFBEncrypter(block Block, iv []byte) Stream {
if len(iv) != block.BlockSize() {
panic("cipher.NewCBFEncrypter: IV length must equal block size")
}
return newCFB(block, iv, false) return newCFB(block, iv, false)
} }
@ -24,6 +27,9 @@ func NewCFBEncrypter(block Block, iv []byte) Stream {
// using the given Block. The iv must be the same length as the Block's block // using the given Block. The iv must be the same length as the Block's block
// size. // size.
func NewCFBDecrypter(block Block, iv []byte) Stream { func NewCFBDecrypter(block Block, iv []byte) Stream {
if len(iv) != block.BlockSize() {
panic("cipher.NewCBFEncrypter: IV length must equal block size")
}
return newCFB(block, iv, true) return newCFB(block, iv, true)
} }

View File

@ -23,7 +23,7 @@ type ctr struct {
// counter mode. The length of iv must be the same as the Block's block size. // counter mode. The length of iv must be the same as the Block's block size.
func NewCTR(block Block, iv []byte) Stream { func NewCTR(block Block, iv []byte) Stream {
if len(iv) != block.BlockSize() { if len(iv) != block.BlockSize() {
panic("cipher.NewCTR: iv length must equal block size") panic("cipher.NewCTR: IV length must equal block size")
} }
return &ctr{ return &ctr{

View File

@ -0,0 +1,283 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cipher_test
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"os"
)
func ExampleNewCBCDecrypter() {
key := []byte("example key 1234")
ciphertext, _ := hex.DecodeString("f363f3ccdcb12bb883abf484ba77d9cd7d32b5baecb3d4b1b3e0e4beffdb3ded")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < aes.BlockSize {
panic("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
// CBC mode always works in whole blocks.
if len(ciphertext)%aes.BlockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(block, iv)
// CryptBlocks can work in-place if the two arguments are the same.
mode.CryptBlocks(ciphertext, ciphertext)
// If the original plaintext lengths are not a multiple of the block
// size, padding would have to be added when encrypting, which would be
// removed at this point. For an example, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. However, it's
// critical to note that ciphertexts must be authenticated (i.e. by
// using crypto/hmac) before being decrypted in order to avoid creating
// a padding oracle.
fmt.Printf("%s\n", ciphertext)
// Output: exampleplaintext
}
func ExampleNewCBCEncrypter() {
key := []byte("example key 1234")
plaintext := []byte("exampleplaintext")
// CBC mode works on blocks so plaintexts may need to be padded to the
// next whole block. For an example of such padding, see
// https://tools.ietf.org/html/rfc5246#section-6.2.3.2. Here we'll
// assume that the plaintext is already of the correct length.
if len(plaintext)%aes.BlockSize != 0 {
panic("plaintext is not a multiple of the block size")
}
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
fmt.Printf("%x\n", ciphertext)
}
func ExampleNewCFBDecrypter() {
key := []byte("example key 1234")
ciphertext, _ := hex.DecodeString("22277966616d9bc47177bd02603d08c9a67d5380d0fe8cf3b44438dff7b9")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
if len(ciphertext) < aes.BlockSize {
panic("ciphertext too short")
}
iv := ciphertext[:aes.BlockSize]
ciphertext = ciphertext[aes.BlockSize:]
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(ciphertext, ciphertext)
fmt.Printf("%s", ciphertext)
// Output: some plaintext
}
func ExampleNewCFBEncrypter() {
key := []byte("example key 1234")
plaintext := []byte("some plaintext")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
}
func ExampleNewCTR() {
key := []byte("example key 1234")
plaintext := []byte("some plaintext")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
// CTR mode is the same for both encryption and decryption, so we can
// also decrypt that ciphertext with NewCTR.
plaintext2 := make([]byte, len(plaintext))
stream = cipher.NewCTR(block, iv)
stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])
fmt.Printf("%s\n", plaintext2)
// Output: some plaintext
}
func ExampleNewOFB() {
key := []byte("example key 1234")
plaintext := []byte("some plaintext")
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// The IV needs to be unique, but not secure. Therefore it's common to
// include it at the beginning of the ciphertext.
ciphertext := make([]byte, aes.BlockSize+len(plaintext))
iv := ciphertext[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
panic(err)
}
stream := cipher.NewOFB(block, iv)
stream.XORKeyStream(ciphertext[aes.BlockSize:], plaintext)
// It's important to remember that ciphertexts must be authenticated
// (i.e. by using crypto/hmac) as well as being encrypted in order to
// be secure.
// OFB mode is the same for both encryption and decryption, so we can
// also decrypt that ciphertext with NewOFB.
plaintext2 := make([]byte, len(plaintext))
stream = cipher.NewOFB(block, iv)
stream.XORKeyStream(plaintext2, ciphertext[aes.BlockSize:])
fmt.Printf("%s\n", plaintext2)
// Output: some plaintext
}
func ExampleStreamReader() {
key := []byte("example key 1234")
inFile, err := os.Open("encrypted-file")
if err != nil {
panic(err)
}
defer inFile.Close()
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// If the key is unique for each ciphertext, then it's ok to use a zero
// IV.
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
outFile, err := os.OpenFile("decrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
panic(err)
}
defer outFile.Close()
reader := &cipher.StreamReader{stream, inFile}
// Copy the input file to the output file, decrypting as we go.
if _, err := io.Copy(outFile, reader); err != nil {
panic(err)
}
// Note that this example is simplistic in that it omits any
// authentication of the encrypted data. It you were actually to use
// StreamReader in this manner, an attacker could flip arbitary bits in
// the output.
}
func ExampleStreamWriter() {
key := []byte("example key 1234")
inFile, err := os.Open("plaintext-file")
if err != nil {
panic(err)
}
defer inFile.Close()
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
// If the key is unique for each ciphertext, then it's ok to use a zero
// IV.
var iv [aes.BlockSize]byte
stream := cipher.NewOFB(block, iv[:])
outFile, err := os.OpenFile("encrypted-file", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
panic(err)
}
defer outFile.Close()
writer := &cipher.StreamWriter{stream, outFile, nil}
// Copy the input file to the output file, encrypting as we go.
if _, err := io.Copy(writer, inFile); err != nil {
panic(err)
}
// Note that this example is simplistic in that it omits any
// authentication of the encrypted data. It you were actually to use
// StreamReader in this manner, an attacker could flip arbitary bits in
// the decrypted result.
}

View File

@ -2,13 +2,27 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as /*
// defined in U.S. Federal Information Processing Standards Publication 198. Package hmac implements the Keyed-Hash Message Authentication Code (HMAC) as
// An HMAC is a cryptographic hash that uses a key to sign a message. defined in U.S. Federal Information Processing Standards Publication 198.
// The receiver verifies the hash by recomputing it using the same key. An HMAC is a cryptographic hash that uses a key to sign a message.
The receiver verifies the hash by recomputing it using the same key.
Receivers should be careful to use Equal to compare MACs in order to avoid
timing side-channels:
// CheckMAC returns true if messageMAC is a valid HMAC tag for message.
func CheckMAC(message, messageMAC, key []byte) bool {
mac := hmac.New(sha256.New, key)
mac.Write(message)
expectedMAC := mac.Sum(nil)
return hmac.Equal(messageMAC, expectedMAC)
}
*/
package hmac package hmac
import ( import (
"crypto/subtle"
"hash" "hash"
) )
@ -57,7 +71,7 @@ func (h *hmac) BlockSize() int { return h.blocksize }
func (h *hmac) Reset() { func (h *hmac) Reset() {
h.inner.Reset() h.inner.Reset()
h.tmpPad(0x36) h.tmpPad(0x36)
h.inner.Write(h.tmp[0:h.blocksize]) h.inner.Write(h.tmp[:h.blocksize])
} }
// New returns a new HMAC hash using the given hash.Hash type and key. // New returns a new HMAC hash using the given hash.Hash type and key.
@ -78,3 +92,11 @@ func New(h func() hash.Hash, key []byte) hash.Hash {
hm.Reset() hm.Reset()
return hm return hm
} }
// Equal compares two MACs for equality without leaking timing information.
func Equal(mac1, mac2 []byte) bool {
// We don't have to be constant time if the lengths of the MACs are
// different as that suggests that a completely different hash function
// was used.
return len(mac1) == len(mac2) && subtle.ConstantTimeCompare(mac1, mac2) == 1
}

View File

@ -491,3 +491,22 @@ func TestHMAC(t *testing.T) {
} }
} }
} }
func TestEqual(t *testing.T) {
a := []byte("test")
b := []byte("test1")
c := []byte("test2")
if !Equal(b, b) {
t.Error("Equal failed with equal arguments")
}
if Equal(a, b) {
t.Error("Equal accepted a prefix of the second argument")
}
if Equal(b, a) {
t.Error("Equal accepted a prefix of the first argument")
}
if Equal(b, c) {
t.Error("Equal accepted unequal slices")
}
}

View File

@ -203,6 +203,8 @@ func block(dig *digest, p []byte) {
// less code and run 1.3x faster if we take advantage of that. // less code and run 1.3x faster if we take advantage of that.
// My apologies. // My apologies.
X = (*[16]uint32)(unsafe.Pointer(&p[0])) X = (*[16]uint32)(unsafe.Pointer(&p[0]))
} else if uintptr(unsafe.Pointer(&p[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
X = (*[16]uint32)(unsafe.Pointer(&p[0]))
} else { } else {
X = &xbuf X = &xbuf
j := 0 j := 0

View File

@ -9,6 +9,7 @@ import (
"fmt" "fmt"
"io" "io"
"testing" "testing"
"unsafe"
) )
type md5Test struct { type md5Test struct {
@ -54,13 +55,19 @@ func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ { for i := 0; i < len(golden); i++ {
g := golden[i] g := golden[i]
c := md5.New() c := md5.New()
for j := 0; j < 3; j++ { buf := make([]byte, len(g.in)+4)
for j := 0; j < 3+4; j++ {
if j < 2 { if j < 2 {
io.WriteString(c, g.in) io.WriteString(c, g.in)
} else { } else if j == 2 {
io.WriteString(c, g.in[0:len(g.in)/2]) io.WriteString(c, g.in[0:len(g.in)/2])
c.Sum(nil) c.Sum(nil)
io.WriteString(c, g.in[len(g.in)/2:]) io.WriteString(c, g.in[len(g.in)/2:])
} else if j > 2 {
// test unaligned write
buf = buf[1:]
copy(buf, g.in)
c.Write(buf[:len(g.in)])
} }
s := fmt.Sprintf("%x", c.Sum(nil)) s := fmt.Sprintf("%x", c.Sum(nil))
if s != g.out { if s != g.out {
@ -80,26 +87,45 @@ func ExampleNew() {
} }
var bench = md5.New() var bench = md5.New()
var buf = makeBuf() var buf = make([]byte, 8192+1)
var sum = make([]byte, bench.Size())
func makeBuf() []byte { func benchmarkSize(b *testing.B, size int, unaligned bool) {
b := make([]byte, 8<<10) b.SetBytes(int64(size))
for i := range b { buf := buf
b[i] = byte(i) if unaligned {
if uintptr(unsafe.Pointer(&buf[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
buf = buf[1:]
}
} }
return b b.ResetTimer()
for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
}
}
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8, false)
} }
func BenchmarkHash1K(b *testing.B) { func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024) benchmarkSize(b, 1024, false)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
} }
func BenchmarkHash8K(b *testing.B) { func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf))) benchmarkSize(b, 8192, false)
for i := 0; i < b.N; i++ { }
bench.Write(buf)
} func BenchmarkHash8BytesUnaligned(b *testing.B) {
benchmarkSize(b, 8, true)
}
func BenchmarkHash1KUnaligned(b *testing.B) {
benchmarkSize(b, 1024, true)
}
func BenchmarkHash8KUnaligned(b *testing.B) {
benchmarkSize(b, 8192, true)
} }

View File

@ -22,6 +22,8 @@ func block(dig *digest, p []byte) {
// less code and run 1.3x faster if we take advantage of that. // less code and run 1.3x faster if we take advantage of that.
// My apologies. // My apologies.
X = (*[16]uint32)(unsafe.Pointer(&p[0])) X = (*[16]uint32)(unsafe.Pointer(&p[0]))
} else if uintptr(unsafe.Pointer(&p[0]))&(unsafe.Alignof(uint32(0))-1) == 0 {
X = (*[16]uint32)(unsafe.Pointer(&p[0]))
} else { } else {
X = &xbuf X = &xbuf
j := 0 j := 0

View File

@ -61,7 +61,7 @@ type PrivateKey struct {
} }
type PrecomputedValues struct { type PrecomputedValues struct {
Dp, Dq *big.Int // D mod (P-1) (or mod Q-1) Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
Qinv *big.Int // Q^-1 mod Q Qinv *big.Int // Q^-1 mod Q
// CRTValues is used for the 3rd and subsequent primes. Due to a // CRTValues is used for the 3rd and subsequent primes. Due to a

View File

@ -116,7 +116,7 @@ func BenchmarkRSA2048Decrypt(b *testing.B) {
} }
priv.Precompute() priv.Precompute()
c := fromBase10("1000") c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313")
b.StartTimer() b.StartTimer()
@ -141,7 +141,7 @@ func Benchmark3PrimeRSA2048Decrypt(b *testing.B) {
} }
priv.Precompute() priv.Precompute()
c := fromBase10("1000") c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313")
b.StartTimer() b.StartTimer()

View File

@ -81,26 +81,26 @@ func ExampleNew() {
} }
var bench = sha1.New() var bench = sha1.New()
var buf = makeBuf() var buf = make([]byte, 8192)
func makeBuf() []byte { func benchmarkSize(b *testing.B, size int) {
b := make([]byte, 8<<10) b.SetBytes(int64(size))
for i := range b { sum := make([]byte, bench.Size())
b[i] = byte(i) for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
} }
return b }
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8)
} }
func BenchmarkHash1K(b *testing.B) { func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024) benchmarkSize(b, 1024)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
} }
func BenchmarkHash8K(b *testing.B) { func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf))) benchmarkSize(b, 8192)
for i := 0; i < b.N; i++ {
bench.Write(buf)
}
} }

View File

@ -16,7 +16,7 @@ const (
) )
func block(dig *digest, p []byte) { func block(dig *digest, p []byte) {
var w [80]uint32 var w [16]uint32
h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4] h0, h1, h2, h3, h4 := dig.h[0], dig.h[1], dig.h[2], dig.h[3], dig.h[4]
for len(p) >= chunk { for len(p) >= chunk {
@ -26,42 +26,56 @@ func block(dig *digest, p []byte) {
j := i * 4 j := i * 4
w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3])
} }
for i := 16; i < 80; i++ {
tmp := w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]
w[i] = tmp<<1 | tmp>>(32-1)
}
a, b, c, d, e := h0, h1, h2, h3, h4 a, b, c, d, e := h0, h1, h2, h3, h4
// Each of the four 20-iteration rounds // Each of the four 20-iteration rounds
// differs only in the computation of f and // differs only in the computation of f and
// the choice of K (_K0, _K1, etc). // the choice of K (_K0, _K1, etc).
for i := 0; i < 20; i++ { i := 0
for ; i < 16; i++ {
f := b&c | (^b)&d f := b&c | (^b)&d
a5 := a<<5 | a>>(32-5) a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30) b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i] + _K0 t := a5 + f + e + w[i&0xf] + _K0
a, b, c, d, e = t, a, b30, c, d a, b, c, d, e = t, a, b30, c, d
} }
for i := 20; i < 40; i++ { for ; i < 20; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b&c | (^b)&d
a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i&0xf] + _K0
a, b, c, d, e = t, a, b30, c, d
}
for ; i < 40; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d f := b ^ c ^ d
a5 := a<<5 | a>>(32-5) a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30) b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i] + _K1 t := a5 + f + e + w[i&0xf] + _K1
a, b, c, d, e = t, a, b30, c, d a, b, c, d, e = t, a, b30, c, d
} }
for i := 40; i < 60; i++ { for ; i < 60; i++ {
f := b&c | b&d | c&d tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := ((b | c) & d) | (b & c)
a5 := a<<5 | a>>(32-5) a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30) b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i] + _K2 t := a5 + f + e + w[i&0xf] + _K2
a, b, c, d, e = t, a, b30, c, d a, b, c, d, e = t, a, b30, c, d
} }
for i := 60; i < 80; i++ { for ; i < 80; i++ {
tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf]
w[i&0xf] = tmp<<1 | tmp>>(32-1)
f := b ^ c ^ d f := b ^ c ^ d
a5 := a<<5 | a>>(32-5) a5 := a<<5 | a>>(32-5)
b30 := b<<30 | b>>(32-30) b30 := b<<30 | b>>(32-30)
t := a5 + f + e + w[i] + _K3 t := a5 + f + e + w[i&0xf] + _K3
a, b, c, d, e = t, a, b30, c, d a, b, c, d, e = t, a, b30, c, d
} }

View File

@ -125,26 +125,26 @@ func TestGolden(t *testing.T) {
} }
var bench = New() var bench = New()
var buf = makeBuf() var buf = make([]byte, 8192)
func makeBuf() []byte { func benchmarkSize(b *testing.B, size int) {
b := make([]byte, 8<<10) b.SetBytes(int64(size))
for i := range b { sum := make([]byte, bench.Size())
b[i] = byte(i) for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
} }
return b }
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8)
} }
func BenchmarkHash1K(b *testing.B) { func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024) benchmarkSize(b, 1024)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
} }
func BenchmarkHash8K(b *testing.B) { func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf))) benchmarkSize(b, 8192)
for i := 0; i < b.N; i++ {
bench.Write(buf)
}
} }

View File

@ -125,26 +125,26 @@ func TestGolden(t *testing.T) {
} }
var bench = New() var bench = New()
var buf = makeBuf() var buf = make([]byte, 8192)
func makeBuf() []byte { func benchmarkSize(b *testing.B, size int) {
b := make([]byte, 8<<10) b.SetBytes(int64(size))
for i := range b { sum := make([]byte, bench.Size())
b[i] = byte(i) for i := 0; i < b.N; i++ {
bench.Reset()
bench.Write(buf[:size])
bench.Sum(sum[:0])
} }
return b }
func BenchmarkHash8Bytes(b *testing.B) {
benchmarkSize(b, 8)
} }
func BenchmarkHash1K(b *testing.B) { func BenchmarkHash1K(b *testing.B) {
b.SetBytes(1024) benchmarkSize(b, 1024)
for i := 0; i < b.N; i++ {
bench.Write(buf[:1024])
}
} }
func BenchmarkHash8K(b *testing.B) { func BenchmarkHash8K(b *testing.B) {
b.SetBytes(int64(len(buf))) benchmarkSize(b, 8192)
for i := 0; i < b.N; i++ {
bench.Write(buf)
}
} }

View File

@ -513,7 +513,7 @@ Again:
// First message, be extra suspicious: // First message, be extra suspicious:
// this might not be a TLS client. // this might not be a TLS client.
// Bail out before reading a full 'body', if possible. // Bail out before reading a full 'body', if possible.
// The current max version is 3.1. // The current max version is 3.1.
// If the version is >= 16.0, it's probably not real. // If the version is >= 16.0, it's probably not real.
// Similarly, a clientHello message encodes in // Similarly, a clientHello message encodes in
// well under a kilobyte. If the length is >= 12 kB, // well under a kilobyte. If the length is >= 12 kB,
@ -604,9 +604,11 @@ Again:
// sendAlert sends a TLS alert message. // sendAlert sends a TLS alert message.
// c.out.Mutex <= L. // c.out.Mutex <= L.
func (c *Conn) sendAlertLocked(err alert) error { func (c *Conn) sendAlertLocked(err alert) error {
c.tmp[0] = alertLevelError switch err {
if err == alertNoRenegotiation { case alertNoRenegotiation, alertCloseNotify:
c.tmp[0] = alertLevelWarning c.tmp[0] = alertLevelWarning
default:
c.tmp[0] = alertLevelError
} }
c.tmp[1] = byte(err) c.tmp[1] = byte(err)
c.writeRecord(recordTypeAlert, c.tmp[0:2]) c.writeRecord(recordTypeAlert, c.tmp[0:2])

View File

@ -246,15 +246,15 @@ var ecdheAESClientScript = [][]byte{
}, },
{ {
0x16, 0x03, 0x01, 0x00, 0x54, 0x02, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 0x54, 0x02, 0x00, 0x00,
0x50, 0x03, 0x01, 0x4f, 0x7f, 0x24, 0x25, 0x10, 0x50, 0x03, 0x01, 0x50, 0x77, 0x31, 0xf7, 0x5b,
0xa8, 0x9d, 0xb1, 0x33, 0xd6, 0x53, 0x81, 0xce, 0xdb, 0x3d, 0x7a, 0x62, 0x76, 0x70, 0x95, 0x33,
0xb0, 0x69, 0xed, 0x1b, 0x9c, 0x5e, 0x40, 0x3a, 0x73, 0x71, 0x13, 0xfe, 0xa3, 0xb1, 0xd8, 0xb3,
0x4d, 0x06, 0xbc, 0xc7, 0x84, 0x51, 0x5a, 0x30, 0x4d, 0x0d, 0xdc, 0xfe, 0x58, 0x6e, 0x6a, 0x3a,
0x40, 0x50, 0x48, 0x20, 0xcd, 0x91, 0x80, 0x08, 0xf9, 0xde, 0xdc, 0x20, 0x8e, 0xfa, 0x3d, 0x60,
0xff, 0x82, 0x38, 0xc6, 0x03, 0x2d, 0x45, 0x4c, 0xd0, 0xda, 0xa4, 0x0e, 0x36, 0xf0, 0xde, 0xb6,
0x91, 0xbb, 0xcc, 0x27, 0x3d, 0x58, 0xff, 0x0d, 0x81, 0xb4, 0x80, 0x5e, 0xf9, 0xd2, 0x4c, 0xec,
0x26, 0x34, 0x7b, 0x48, 0x7a, 0xce, 0x25, 0x20, 0xd1, 0x9c, 0x2a, 0x81, 0xc3, 0x36, 0x0b, 0x0f,
0x90, 0x0f, 0x35, 0x9f, 0xc0, 0x13, 0x00, 0x00, 0x4a, 0x3d, 0xdf, 0x75, 0xc0, 0x13, 0x00, 0x00,
0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
0x02, 0x16, 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00, 0x02, 0x16, 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00,
0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, 0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f,
@ -329,23 +329,23 @@ var ecdheAESClientScript = [][]byte{
0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, 0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8,
0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, 0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16,
0x03, 0x01, 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87, 0x03, 0x01, 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87,
0x03, 0x00, 0x17, 0x41, 0x04, 0x0b, 0xe5, 0x39, 0x03, 0x00, 0x17, 0x41, 0x04, 0xec, 0x06, 0x1f,
0xde, 0x17, 0x7a, 0xaf, 0x96, 0xd5, 0x16, 0x01, 0xa0, 0x5e, 0x29, 0x49, 0x71, 0x8b, 0x04, 0x9f,
0xa8, 0x06, 0x80, 0x98, 0x75, 0x52, 0x56, 0x92, 0x47, 0x87, 0xb1, 0xcb, 0xae, 0x57, 0x8f, 0xd7,
0x15, 0xf9, 0x8d, 0xc0, 0x98, 0x62, 0xed, 0x54, 0xf6, 0xf8, 0x59, 0x74, 0x64, 0x5d, 0x3a, 0x08,
0xb7, 0xef, 0x03, 0x11, 0x34, 0x82, 0x65, 0xd1, 0xaf, 0x20, 0xc6, 0xd9, 0xfc, 0x5e, 0x36, 0x8b,
0xde, 0x25, 0x15, 0x4c, 0xf3, 0xdf, 0x4d, 0xbd, 0x62, 0x0e, 0xdb, 0xee, 0xd8, 0xcd, 0xef, 0x25,
0x6c, 0xed, 0x3d, 0xd6, 0x04, 0xcc, 0xd1, 0xf7, 0x8a, 0x38, 0x88, 0x2d, 0x5c, 0x71, 0x50, 0x22,
0x6d, 0x32, 0xb1, 0x1c, 0x59, 0xca, 0xfb, 0xbc, 0xda, 0x3f, 0x94, 0x06, 0xc9, 0x68, 0x5b, 0x78,
0x61, 0xeb, 0x4b, 0xe6, 0x00, 0x00, 0x40, 0x3e, 0x3d, 0x95, 0xca, 0x54, 0x44, 0x00, 0x40, 0x36,
0xe6, 0x23, 0x54, 0x61, 0x3f, 0x63, 0x16, 0xeb, 0xcf, 0x10, 0x81, 0xb4, 0x32, 0x45, 0x3c, 0xa5,
0x5c, 0xc3, 0xba, 0x8a, 0x19, 0x13, 0x60, 0x9f, 0x2d, 0x3e, 0xb0, 0xf8, 0xf4, 0x51, 0xf5, 0x28,
0x23, 0xbf, 0x36, 0x1a, 0x32, 0x7a, 0xae, 0x34, 0x09, 0x85, 0x71, 0xa6, 0x79, 0x71, 0x4b, 0x4e,
0x7f, 0x2f, 0x89, 0x85, 0xe1, 0x0e, 0x93, 0xd7, 0xda, 0x32, 0x5a, 0xc7, 0xb3, 0x57, 0xfd, 0xe8,
0xf0, 0xab, 0xa1, 0x0d, 0x54, 0x95, 0x79, 0x0b, 0x12, 0xab, 0xd8, 0x29, 0xfb, 0x8b, 0x43, 0x8f,
0xb4, 0xf1, 0x1c, 0x1d, 0x0f, 0x8c, 0x16, 0xec, 0x7e, 0x27, 0x63, 0x91, 0x84, 0x9c, 0x51, 0x0c,
0x82, 0x60, 0xee, 0xa3, 0x71, 0x2f, 0xaf, 0x3e, 0x26, 0x7e, 0x36, 0x3b, 0x37, 0x8d, 0x8f, 0x9e,
0xf1, 0xbd, 0xb5, 0x1b, 0x7f, 0xe0, 0xd2, 0x16, 0xe2, 0x82, 0x62, 0xbb, 0xe5, 0xdf, 0xfc, 0x16,
0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
}, },
{ {
@ -359,34 +359,34 @@ var ecdheAESClientScript = [][]byte{
0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49, 0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b, 0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01, 0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01,
0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x09, 0xac, 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x9a, 0xaa,
0xbe, 0x94, 0x75, 0x4d, 0x73, 0x45, 0xbd, 0xa8, 0xca, 0x5b, 0x57, 0xae, 0x34, 0x92, 0x80, 0x45,
0x0c, 0xe3, 0x5f, 0x72, 0x0b, 0x40, 0x4f, 0xd0, 0x7f, 0xe6, 0xf9, 0x09, 0x19, 0xd0, 0xf0, 0x1e,
0xd2, 0xcb, 0x16, 0x50, 0xfe, 0xdd, 0x1a, 0x33, 0x4b, 0xc3, 0xda, 0x71, 0xce, 0x34, 0x33, 0x56,
0x5c, 0x18, 0x37, 0x98, 0x42, 0xfc, 0x25, 0x42, 0x9f, 0x20, 0x9f, 0xf9, 0xa8, 0x62, 0x6c, 0x38,
0x33, 0xce, 0x60, 0xcf, 0x8e, 0x95, 0x6e, 0x48, 0x1b, 0x41, 0xf5, 0x54, 0xf2, 0x79, 0x42, 0x6c,
0xed, 0x00, 0x35, 0x50, 0x26, 0x7f, 0xb5, 0x0e, 0xe7, 0xe1, 0xbc, 0x54,
}, },
{ {
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
0x01, 0x00, 0x30, 0xf6, 0x6a, 0xdb, 0x83, 0xd4, 0x01, 0x00, 0x30, 0x62, 0x82, 0x41, 0x75, 0x2b,
0x3c, 0x77, 0x52, 0xad, 0xc0, 0x0f, 0x3a, 0x2c, 0xee, 0x0f, 0xdc, 0x6c, 0x48, 0x5a, 0x63, 0xd6,
0x42, 0xb9, 0x60, 0x4b, 0xb2, 0xf6, 0x84, 0xfd, 0xcb, 0x0a, 0xfd, 0x0a, 0x0e, 0xde, 0x8b, 0x41,
0x4e, 0x96, 0xfc, 0x15, 0xe7, 0x94, 0x25, 0xb0, 0x19, 0x0c, 0x13, 0x6b, 0x12, 0xd1, 0xc2, 0x53,
0x59, 0x9f, 0xdd, 0xb6, 0x58, 0x03, 0x13, 0x8d, 0xeb, 0x1e, 0xf3, 0x7a, 0xbf, 0x23, 0xc5, 0xa6,
0xeb, 0xb0, 0xad, 0x30, 0x31, 0x58, 0x6c, 0xa0, 0x81, 0xa1, 0xdb, 0xab, 0x2f, 0x2c, 0xbc, 0x35,
0x8f, 0x57, 0x50, 0x96, 0x72, 0x83,
}, },
{ {
0x17, 0x03, 0x01, 0x00, 0x20, 0xab, 0x64, 0x3d, 0x17, 0x03, 0x01, 0x00, 0x20, 0xaf, 0x5d, 0x35,
0x79, 0x69, 0x3e, 0xba, 0xc4, 0x24, 0x7b, 0xe5, 0x57, 0x10, 0x60, 0xb3, 0x25, 0x7c, 0x26, 0x0f,
0xe5, 0x23, 0x66, 0x6f, 0x32, 0xdf, 0x50, 0x7c, 0xf3, 0x5e, 0xb3, 0x0d, 0xad, 0x14, 0x53, 0xcc,
0x06, 0x2a, 0x02, 0x82, 0x79, 0x40, 0xdb, 0xb1, 0x0c, 0x08, 0xd9, 0xa2, 0x67, 0xab, 0xf4, 0x03,
0x04, 0xc0, 0x2b, 0xdc, 0x3a, 0x15, 0x03, 0x01, 0x17, 0x20, 0xf1, 0x7e, 0xca, 0x15, 0x03, 0x01,
0x00, 0x20, 0xf8, 0xad, 0xca, 0xd7, 0x96, 0xf0, 0x00, 0x20, 0x30, 0xd0, 0xc1, 0xfb, 0x5f, 0xa6,
0xd6, 0xa3, 0x62, 0xe1, 0x03, 0x44, 0xdb, 0xd0, 0x1b, 0xb4, 0x48, 0xc2, 0x0b, 0x98, 0xa8, 0x88,
0xc9, 0x63, 0x3e, 0x1b, 0x70, 0x41, 0x57, 0x0c, 0x7a, 0xba, 0xdf, 0x36, 0x06, 0xd8, 0xcc, 0xe9,
0xd8, 0x8e, 0x71, 0x49, 0x68, 0xe3, 0x04, 0x53, 0x34, 0xdd, 0x64, 0xc8, 0x73, 0xc5, 0xa2, 0x34,
0x5a, 0xbe, 0x64, 0xb7,
}, },
} }

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
masterSecret := masterFromPreMasterSecret(test.version, in, clientRandom, serverRandom) masterSecret := masterFromPreMasterSecret(test.version, in, clientRandom, serverRandom)
if s := hex.EncodeToString(masterSecret); s != test.masterSecret { if s := hex.EncodeToString(masterSecret); s != test.masterSecret {
t.Errorf("#%d: bad master secret %s, want %s", s, test.masterSecret) t.Errorf("#%d: bad master secret %s, want %s", i, s, test.masterSecret)
continue continue
} }

View File

@ -6,6 +6,8 @@
package tls package tls
import ( import (
"crypto"
"crypto/ecdsa"
"crypto/rsa" "crypto/rsa"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
@ -153,30 +155,16 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
err = errors.New("crypto/tls: failed to parse key PEM data") err = errors.New("crypto/tls: failed to parse key PEM data")
return return
} }
if keyDERBlock.Type != "CERTIFICATE" { if strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
break break
} }
} }
// OpenSSL 0.9.8 generates PKCS#1 private keys by default, while cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
// OpenSSL 1.0.0 generates PKCS#8 keys. We try both. if err != nil {
var key *rsa.PrivateKey return
if key, err = x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes); err != nil {
var privKey interface{}
if privKey, err = x509.ParsePKCS8PrivateKey(keyDERBlock.Bytes); err != nil {
err = errors.New("crypto/tls: failed to parse key: " + err.Error())
return
}
var ok bool
if key, ok = privKey.(*rsa.PrivateKey); !ok {
err = errors.New("crypto/tls: found non-RSA private key in PKCS#8 wrapping")
return
}
} }
cert.PrivateKey = key
// We don't need to parse the public key for TLS, but we so do anyway // We don't need to parse the public key for TLS, but we so do anyway
// to check that it looks sane and matches the private key. // to check that it looks sane and matches the private key.
x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
@ -184,10 +172,54 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
return return
} }
if x509Cert.PublicKeyAlgorithm != x509.RSA || x509Cert.PublicKey.(*rsa.PublicKey).N.Cmp(key.PublicKey.N) != 0 { switch pub := x509Cert.PublicKey.(type) {
err = errors.New("crypto/tls: private key does not match public key") case *rsa.PublicKey:
priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
if !ok {
err = errors.New("crypto/tls: private key type does not match public key type")
return
}
if pub.N.Cmp(priv.N) != 0 {
err = errors.New("crypto/tls: private key does not match public key")
return
}
case *ecdsa.PublicKey:
priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
if !ok {
err = errors.New("crypto/tls: private key type does not match public key type")
return
}
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
err = errors.New("crypto/tls: private key does not match public key")
return
}
default:
err = errors.New("crypto/tls: unknown public key algorithm")
return return
} }
return return
} }
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
return key, nil
default:
return nil, errors.New("crypto/tls: found unknown private key type in PKCS#8 wrapping")
}
}
if key, err := x509.ParseECPrivateKey(der); err == nil {
return key, nil
}
return nil, errors.New("crypto/tls: failed to parse private key")
}

View File

@ -8,7 +8,7 @@ import (
"testing" "testing"
) )
var certPEM = `-----BEGIN CERTIFICATE----- var rsaCertPEM = `-----BEGIN CERTIFICATE-----
MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV MIIB0zCCAX2gAwIBAgIJAI/M7BYjwB+uMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF aWRnaXRzIFB0eSBMdGQwHhcNMTIwOTEyMjE1MjAyWhcNMTUwOTEyMjE1MjAyWjBF
@ -22,7 +22,7 @@ r5QuVbpQhH6u+0UgcW0jp9QwpxoPTLTWGXEWBBBurxFwiCBhkQ+V
-----END CERTIFICATE----- -----END CERTIFICATE-----
` `
var keyPEM = `-----BEGIN RSA PRIVATE KEY----- var rsaKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N 6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
@ -33,15 +33,61 @@ D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
` `
func TestX509KeyPair(t *testing.T) { var ecdsaCertPEM = `-----BEGIN CERTIFICATE-----
_, err := X509KeyPair([]byte(keyPEM+certPEM), []byte(keyPEM+certPEM)) MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
if err != nil { EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
t.Errorf("Failed to load key followed by cert: %s", err) eSBMdGQwHhcNMTIxMTE0MTI0MDQ4WhcNMTUxMTE0MTI0MDQ4WjBFMQswCQYDVQQG
} EwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lk
Z2l0cyBQdHkgTHRkMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBY9+my9OoeSUR
lDQdV/x8LsOuLilthhiS1Tz4aGDHIPwC1mlvnf7fg5lecYpMCrLLhauAc1UJXcgl
01xoLuzgtAEAgv2P/jgytzRSpUYvgLBt1UA0leLYBy6mQQbrNEuqT3INapKIcUv8
XxYP0xMEUksLPq6Ca+CRSqTtrd/23uTnapkwCQYHKoZIzj0EAQOBigAwgYYCQXJo
A7Sl2nLVf+4Iu/tAX/IF4MavARKC4PPHK3zfuGfPR3oCCcsAoz3kAzOeijvd0iXb
H5jBImIxPL4WxQNiBTexAkF8D1EtpYuWdlVQ80/h/f4pBcGiXPqX5h2PQSQY7hP1
+jwM1FGS4fREIOvlBYr/SzzQRtwrvrzGYxDEDbsC0ZGRnA==
-----END CERTIFICATE-----
`
_, err = X509KeyPair([]byte(certPEM+keyPEM), []byte(certPEM+keyPEM)) var ecdsaKeyPEM = `-----BEGIN EC PARAMETERS-----
if err != nil { BgUrgQQAIw==
t.Errorf("Failed to load cert followed by key: %s", err) -----END EC PARAMETERS-----
println(err.Error()) -----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBrsoKp0oqcv6/JovJJDoDVSGWdirrkgCWxrprGlzB9o0X8fV675X0
NwuBenXFfeZvVcwluO7/Q9wkYoPd/t3jGImgBwYFK4EEACOhgYkDgYYABAFj36bL
06h5JRGUNB1X/Hwuw64uKW2GGJLVPPhoYMcg/ALWaW+d/t+DmV5xikwKssuFq4Bz
VQldyCXTXGgu7OC0AQCC/Y/+ODK3NFKlRi+AsG3VQDSV4tgHLqZBBus0S6pPcg1q
kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ==
-----END EC PRIVATE KEY-----
`
var keyPairTests = []struct {
algo string
cert *string
key *string
}{
{"ECDSA", &ecdsaCertPEM, &ecdsaKeyPEM},
{"RSA", &rsaCertPEM, &rsaKeyPEM},
}
func TestX509KeyPair(t *testing.T) {
var pem []byte
for _, test := range keyPairTests {
pem = []byte(*test.cert + *test.key)
if _, err := X509KeyPair(pem, pem); err != nil {
t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err)
}
pem = []byte(*test.key + *test.cert)
if _, err := X509KeyPair(pem, pem); err != nil {
t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err)
}
}
}
func TestX509MixedKeyPair(t *testing.T) {
if _, err := X509KeyPair([]byte(rsaCertPEM), []byte(ecdsaKeyPEM)); err == nil {
t.Error("Load of RSA certificate succeeded with ECDSA private key")
}
if _, err := X509KeyPair([]byte(ecdsaCertPEM), []byte(rsaKeyPEM)); err == nil {
t.Error("Load of ECDSA certificate succeeded with RSA private key")
} }
} }

View File

@ -103,7 +103,7 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
} }
// Subjects returns a list of the DER-encoded subjects of // Subjects returns a list of the DER-encoded subjects of
// all of the certificates in the pool. // all of the certificates in the pool.
func (s *CertPool) Subjects() (res [][]byte) { func (s *CertPool) Subjects() (res [][]byte) {
res = make([][]byte, len(s.certs)) res = make([][]byte, len(s.certs))
for i, c := range s.certs { for i, c := range s.certs {

View File

@ -16,13 +16,64 @@ import (
"encoding/hex" "encoding/hex"
"encoding/pem" "encoding/pem"
"errors" "errors"
"io"
"strings" "strings"
) )
// rfc1423Algos represents how to create a block cipher for a decryption mode. type PEMCipher int
// Possible values for the EncryptPEMBlock encryption algorithm.
const (
_ PEMCipher = iota
PEMCipherDES
PEMCipher3DES
PEMCipherAES128
PEMCipherAES192
PEMCipherAES256
)
// rfc1423Algo holds a method for enciphering a PEM block.
type rfc1423Algo struct { type rfc1423Algo struct {
cipherFunc func([]byte) (cipher.Block, error) cipher PEMCipher
name string
cipherFunc func(key []byte) (cipher.Block, error)
keySize int keySize int
blockSize int
}
// rfc1423Algos holds a slice of the possible ways to encrypt a PEM
// block. The ivSize numbers were taken from the OpenSSL source.
var rfc1423Algos = []rfc1423Algo{{
cipher: PEMCipherDES,
name: "DES-CBC",
cipherFunc: des.NewCipher,
keySize: 8,
blockSize: des.BlockSize,
}, {
cipher: PEMCipher3DES,
name: "DES-EDE3-CBC",
cipherFunc: des.NewTripleDESCipher,
keySize: 24,
blockSize: des.BlockSize,
}, {
cipher: PEMCipherAES128,
name: "AES-128-CBC",
cipherFunc: aes.NewCipher,
keySize: 16,
blockSize: aes.BlockSize,
}, {
cipher: PEMCipherAES192,
name: "AES-192-CBC",
cipherFunc: aes.NewCipher,
keySize: 24,
blockSize: aes.BlockSize,
}, {
cipher: PEMCipherAES256,
name: "AES-256-CBC",
cipherFunc: aes.NewCipher,
keySize: 32,
blockSize: aes.BlockSize,
},
} }
// deriveKey uses a key derivation function to stretch the password into a key // deriveKey uses a key derivation function to stretch the password into a key
@ -41,20 +92,9 @@ func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
digest = hash.Sum(digest[:0]) digest = hash.Sum(digest[:0])
copy(out[i:], digest) copy(out[i:], digest)
} }
return out return out
} }
// rfc1423Algos is a mapping of encryption algorithm to an rfc1423Algo that can
// create block ciphers for that mode.
var rfc1423Algos = map[string]rfc1423Algo{
"DES-CBC": {des.NewCipher, 8},
"DES-EDE3-CBC": {des.NewTripleDESCipher, 24},
"AES-128-CBC": {aes.NewCipher, 16},
"AES-192-CBC": {aes.NewCipher, 24},
"AES-256-CBC": {aes.NewCipher, 32},
}
// IsEncryptedPEMBlock returns if the PEM block is password encrypted. // IsEncryptedPEMBlock returns if the PEM block is password encrypted.
func IsEncryptedPEMBlock(b *pem.Block) bool { func IsEncryptedPEMBlock(b *pem.Block) bool {
_, ok := b.Headers["DEK-Info"] _, ok := b.Headers["DEK-Info"]
@ -81,17 +121,16 @@ func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
} }
mode, hexIV := dek[:idx], dek[idx+1:] mode, hexIV := dek[:idx], dek[idx+1:]
ciph := cipherByName(mode)
if ciph == nil {
return nil, errors.New("x509: unknown encryption mode")
}
iv, err := hex.DecodeString(hexIV) iv, err := hex.DecodeString(hexIV)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if len(iv) < 8 { if len(iv) != ciph.blockSize {
return nil, errors.New("x509: not enough bytes in IV") return nil, errors.New("x509: incorrect IV size")
}
ciph, ok := rfc1423Algos[mode]
if !ok {
return nil, errors.New("x509: unknown encryption mode")
} }
// Based on the OpenSSL implementation. The salt is the first 8 bytes // Based on the OpenSSL implementation. The salt is the first 8 bytes
@ -107,27 +146,88 @@ func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
dec.CryptBlocks(data, b.Bytes) dec.CryptBlocks(data, b.Bytes)
// Blocks are padded using a scheme where the last n bytes of padding are all // Blocks are padded using a scheme where the last n bytes of padding are all
// equal to n. It can pad from 1 to 8 bytes inclusive. See RFC 1423. // equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
// For example: // For example:
// [x y z 2 2] // [x y z 2 2]
// [x y 7 7 7 7 7 7 7] // [x y 7 7 7 7 7 7 7]
// If we detect a bad padding, we assume it is an invalid password. // If we detect a bad padding, we assume it is an invalid password.
dlen := len(data) dlen := len(data)
if dlen == 0 { if dlen == 0 || dlen%ciph.blockSize != 0 {
return nil, errors.New("x509: invalid padding") return nil, errors.New("x509: invalid padding")
} }
last := data[dlen-1] last := int(data[dlen-1])
if dlen < int(last) { if dlen < last {
return nil, IncorrectPasswordError return nil, IncorrectPasswordError
} }
if last == 0 || last > 8 { if last == 0 || last > ciph.blockSize {
return nil, IncorrectPasswordError return nil, IncorrectPasswordError
} }
for _, val := range data[dlen-int(last):] { for _, val := range data[dlen-last:] {
if val != last { if int(val) != last {
return nil, IncorrectPasswordError return nil, IncorrectPasswordError
} }
} }
return data[:dlen-last], nil
return data[:dlen-int(last)], nil }
// EncryptPEMBlock returns a PEM block of the specified type holding the
// given DER-encoded data encrypted with the specified algorithm and
// password.
func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
ciph := cipherByKey(alg)
if ciph == nil {
return nil, errors.New("x509: unknown encryption mode")
}
iv := make([]byte, ciph.blockSize)
if _, err := io.ReadFull(rand, iv); err != nil {
return nil, errors.New("x509: cannot generate IV: " + err.Error())
}
// The salt is the first 8 bytes of the initialization vector,
// matching the key derivation in DecryptPEMBlock.
key := ciph.deriveKey(password, iv[:8])
block, err := ciph.cipherFunc(key)
if err != nil {
return nil, err
}
enc := cipher.NewCBCEncrypter(block, iv)
pad := ciph.blockSize - len(data)%ciph.blockSize
encrypted := make([]byte, len(data), len(data)+pad)
// We could save this copy by encrypting all the whole blocks in
// the data separately, but it doesn't seem worth the additional
// code.
copy(encrypted, data)
// See RFC 1423, section 1.1
for i := 0; i < pad; i++ {
encrypted = append(encrypted, byte(pad))
}
enc.CryptBlocks(encrypted, encrypted)
return &pem.Block{
Type: blockType,
Headers: map[string]string{
"Proc-Type": "4,ENCRYPTED",
"DEK-Info": ciph.name + "," + hex.EncodeToString(iv),
},
Bytes: encrypted,
}, nil
}
func cipherByName(name string) *rfc1423Algo {
for i := range rfc1423Algos {
alg := &rfc1423Algos[i]
if alg.name == name {
return alg
}
}
return nil
}
func cipherByKey(key PEMCipher) *rfc1423Algo {
for i := range rfc1423Algos {
alg := &rfc1423Algos[i]
if alg.cipher == key {
return alg
}
}
return nil
} }

View File

@ -5,34 +5,79 @@
package x509 package x509
import ( import (
"bytes"
"crypto/rand"
"encoding/base64"
"encoding/pem" "encoding/pem"
"testing" "testing"
) )
func TestDecrypt(t *testing.T) { func TestDecrypt(t *testing.T) {
for _, data := range testData { for i, data := range testData {
t.Logf("test %d. %s", i, data.kind)
block, rest := pem.Decode(data.pemData) block, rest := pem.Decode(data.pemData)
if len(rest) > 0 { if len(rest) > 0 {
t.Error(data.kind, "extra data") t.Error("extra data")
} }
der, err := DecryptPEMBlock(block, data.password) der, err := DecryptPEMBlock(block, data.password)
if err != nil { if err != nil {
t.Error(data.kind, err) t.Error("decrypt failed: ", err)
continue continue
} }
if _, err := ParsePKCS1PrivateKey(der); err != nil { if _, err := ParsePKCS1PrivateKey(der); err != nil {
t.Error(data.kind, "Invalid private key") t.Error("invalid private key: ", err)
}
plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
if err != nil {
t.Fatal("cannot decode test DER data: ", err)
}
if !bytes.Equal(der, plainDER) {
t.Error("data mismatch")
}
}
}
func TestEncrypt(t *testing.T) {
for i, data := range testData {
t.Logf("test %d. %s", i, data.kind)
plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
if err != nil {
t.Fatal("cannot decode test DER data: ", err)
}
password := []byte("kremvax1")
block, err := EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", plainDER, password, data.kind)
if err != nil {
t.Error("encrypt: ", err)
continue
}
if !IsEncryptedPEMBlock(block) {
t.Error("PEM block does not appear to be encrypted")
}
if block.Type != "RSA PRIVATE KEY" {
t.Errorf("unexpected block type; got %q want %q", block.Type, "RSA PRIVATE KEY")
}
if block.Headers["Proc-Type"] != "4,ENCRYPTED" {
t.Errorf("block does not have correct Proc-Type header")
}
der, err := DecryptPEMBlock(block, password)
if err != nil {
t.Error("decrypt: ", err)
continue
}
if !bytes.Equal(der, plainDER) {
t.Errorf("data mismatch")
} }
} }
} }
var testData = []struct { var testData = []struct {
kind string kind PEMCipher
password []byte password []byte
pemData []byte pemData []byte
plainDER string
}{ }{
{ {
kind: "DES-CBC", kind: PEMCipherDES,
password: []byte("asdf"), password: []byte("asdf"),
pemData: []byte(` pemData: []byte(`
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@ -47,9 +92,17 @@ XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm
4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD 4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD
r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug== r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug==
-----END RSA PRIVATE KEY-----`), -----END RSA PRIVATE KEY-----`),
plainDER: `
MIIBPAIBAAJBAPASZe+tCPU6p80AjHhDkVsLYa51D35e/YGa8QcZyooeZM8EHozo
KD0fNiKI+53bHdy07N+81VQ8/ejPcRoXPlsCAwEAAQJBAMTxIuSq27VpR+zZ7WJf
c6fvv1OBvpMZ0/d1pxL/KnOAgq2rD5hDtk9b0LGhTPgQAmrrMTKuSeGoIuYE+gKQ
QvkCIQD+GC1m+/do+QRurr0uo46Kx1LzLeSCrjBk34wiOp2+dwIhAPHfTLRXS2fv
7rljm0bYa4+eDZpz+E8RcXEgzhhvcQQ9AiAI5eHZJGOyml3MXnQjiPi55WcDOw0w
glcRgT6QCEtz2wIhANSyqaFtosIkHKqrDUGfz/bb5tqMYTAnBruVPaf/WEOBAiEA
9xORWeRG1tRpso4+dYy4KdDkuLPIO01KY6neYGm3BCM=`,
}, },
{ {
kind: "DES-EDE3-CBC", kind: PEMCipher3DES,
password: []byte("asdf"), password: []byte("asdf"),
pemData: []byte(` pemData: []byte(`
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@ -64,9 +117,17 @@ ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w
3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq 3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq
gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk= gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk=
-----END RSA PRIVATE KEY-----`), -----END RSA PRIVATE KEY-----`),
plainDER: `
MIIBOwIBAAJBANOCXKdoNS/iP/MAbl9cf1/SF3P+Ns7ZeNL27CfmDh0O6Zduaax5
NBiumd2PmjkaCu7lQ5JOibHfWn+xJsc3kw0CAwEAAQJANX/W8d1Q/sCqzkuAn4xl
B5a7qfJWaLHndu1QRLNTRJPn0Ee7OKJ4H0QKOhQM6vpjRrz+P2u9thn6wUxoPsef
QQIhAP/jCkfejFcy4v15beqKzwz08/tslVjF+Yq41eJGejmxAiEA05pMoqfkyjcx
fyvGhpoOyoCp71vSGUfR2I9CR65oKh0CIC1Msjs66LlfJtQctRq6bCEtFCxEcsP+
eEjYo/Sk6WphAiEAxpgWPMJeU/shFT28gS+tmhjPZLpEoT1qkVlC14u0b3ECIQDX
tZZZxCtPAm7shftEib0VU77Lk8MsXJcx2C4voRsjEw==`,
}, },
{ {
kind: "AES-128-CBC", kind: PEMCipherAES128,
password: []byte("asdf"), password: []byte("asdf"),
pemData: []byte(` pemData: []byte(`
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@ -81,9 +142,17 @@ GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D
080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo 080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo
AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0= AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0=
-----END RSA PRIVATE KEY-----`), -----END RSA PRIVATE KEY-----`),
plainDER: `
MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT
cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3
k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE
cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB
kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78
XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR
B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`,
}, },
{ {
kind: "AES-192-CBC", kind: PEMCipherAES192,
password: []byte("asdf"), password: []byte("asdf"),
pemData: []byte(` pemData: []byte(`
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@ -98,9 +167,17 @@ ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6
xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t
Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw= Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw=
-----END RSA PRIVATE KEY-----`), -----END RSA PRIVATE KEY-----`),
plainDER: `
MIIBOwIBAAJBAMGcRrZiNNmtF20zyS6MQ7pdGx17aFDl+lTl+qnLuJRUCMUG05xs
OmxmL/O1Qlf+bnqR8Bgg65SfKg21SYuLhiMCAwEAAQJBAL94uuHyO4wux2VC+qpj
IzPykjdU7XRcDHbbvksf4xokSeUFjjD3PB0Qa83M94y89ZfdILIqS9x5EgSB4/lX
qNkCIQD6cCIqLfzq/lYbZbQgAAjpBXeQVYsbvVtJrPrXJAlVVQIhAMXpDKMeFPMn
J0g2rbx1gngx0qOa5r5iMU5w/noN4W2XAiBjf+WzCG5yFvazD+dOx3TC0A8+4x3P
uZ3pWbaXf5PNuQIgAcdXarvhelH2w2piY1g3BPeFqhzBSCK/yLGxR82KIh8CIQDD
+qGKsd09NhQ/G27y/DARzOYtml1NvdmCQAgsDIIOLA==`,
}, },
{ {
kind: "AES-256-CBC", kind: PEMCipherAES256,
password: []byte("asdf"), password: []byte("asdf"),
pemData: []byte(` pemData: []byte(`
-----BEGIN RSA PRIVATE KEY----- -----BEGIN RSA PRIVATE KEY-----
@ -115,5 +192,32 @@ Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/
sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk
clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0= clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0=
-----END RSA PRIVATE KEY-----`), -----END RSA PRIVATE KEY-----`),
plainDER: `
MIIBOgIBAAJBAKy3GFkstoCHIEeUU/qO8207m8WSrjksR+p9B4tf1w5k+2O1V/GY
AQ5WFCApItcOkQe/I0yZZJk/PmCqMzSxrc8CAwEAAQJAOCAz0F7AW9oNelVQSP8F
Sfzx7O1yom+qWyAQQJF/gFR11gpf9xpVnnyu1WxIRnDUh1LZwUsjwlDYb7MB74id
oQIhANPcOiLwOPT4sIUpRM5HG6BF1BI7L77VpyGVk8xNP7X/AiEA0LMHZtk4I+lJ
nClgYp4Yh2JZ1Znbu7IoQMCEJCjwKDECIGd8Dzm5tViTkUW6Hs3Tlf73nNs65duF
aRnSglss8I3pAiEAonEnKruawgD8RavDFR+fUgmQiPz4FnGGeVgfwpGG1JECIBYq
PXHYtPqxQIbD2pScR5qum7iGUh11lEUPkmt+2uqS`,
},
{
// generated with:
// openssl genrsa -aes128 -passout pass:asdf -out server.orig.key 128
kind: PEMCipherAES128,
password: []byte("asdf"),
pemData: []byte(`
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7
6ei/MlytjE0FFgZOGQ+jrwomKfpl8kdefeE0NSt/DMRrw8OacHAzBNi3pPEa0eX3
eND9l7C9meCirWovjj9QWVHrXyugFuDIqgdhQ8iHTgCfF3lrmcttVrbIfMDw+smD
hTP8O1mS/MHl92NE0nhv0w==
-----END RSA PRIVATE KEY-----`),
plainDER: `
MGMCAQACEQC6ssxmYuauuHGOCDAI54RdAgMBAAECEQCWIn6Yv2O+kBcDF7STctKB
AgkA8SEfu/2i3g0CCQDGNlXbBHX7kQIIK3Ww5o0cYbECCQDCimPb0dYGsQIIeQ7A
jryIst8=`,
}, },
} }

View File

@ -11,8 +11,9 @@ import (
"fmt" "fmt"
) )
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See // pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn. // ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
// and RFC5208.
type pkcs8 struct { type pkcs8 struct {
Version int Version int
Algo pkix.AlgorithmIdentifier Algo pkix.AlgorithmIdentifier
@ -21,7 +22,7 @@ type pkcs8 struct {
} }
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See // ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
// http://www.rsa.com/rsalabs/node.asp?id=2130 // http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) { func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
var privKey pkcs8 var privKey pkcs8
if _, err := asn1.Unmarshal(der, &privKey); err != nil { if _, err := asn1.Unmarshal(der, &privKey); err != nil {
@ -34,6 +35,19 @@ func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error()) return nil, errors.New("crypto/x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
} }
return key, nil return key, nil
case privKey.Algo.Algorithm.Equal(oidPublicKeyECDSA):
bytes := privKey.Algo.Parameters.FullBytes
namedCurveOID := new(asn1.ObjectIdentifier)
if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
namedCurveOID = nil
}
key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
if err != nil {
return nil, errors.New("crypto/x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
}
return key, nil
default: default:
return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm) return nil, fmt.Errorf("crypto/x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
} }

View File

@ -9,12 +9,20 @@ import (
"testing" "testing"
) )
var pkcs8PrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031` var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
// Generated using:
// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
var pkcs8ECPrivateKeyHex = `3081ed020100301006072a8648ce3d020106052b810400230481d53081d20201010441850d81618c5da1aec74c2eed608ba816038506975e6427237c2def150c96a3b13efbfa1f89f1be15cdf4d0ac26422e680e65a0ddd4ad3541ad76165fbf54d6e34ba18189038186000400da97bcedba1eb6d30aeb93c9f9a1454598fa47278df27d6f60ea73eb672d8dc528a9b67885b5b5dcef93c9824f7449ab512ee6a27e76142f56b94b474cfd697e810046c8ca70419365245c1d7d44d0db82c334073835d002232714548abbae6e5700f5ef315ee08b929d8581383dcf2d1c98c2f8a9fccbf79c9579f7b2fd8a90115ac2`
func TestPKCS8(t *testing.T) { func TestPKCS8(t *testing.T) {
derBytes, _ := hex.DecodeString(pkcs8PrivateKeyHex) derBytes, _ := hex.DecodeString(pkcs8RSAPrivateKeyHex)
_, err := ParsePKCS8PrivateKey(derBytes) if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
if err != nil { t.Errorf("failed to decode PKCS8 with RSA private key: %s", err)
t.Errorf("failed to decode PKCS8 key: %s", err) }
derBytes, _ = hex.DecodeString(pkcs8ECPrivateKeyHex)
if _, err := ParsePKCS8PrivateKey(derBytes); err != nil {
t.Errorf("failed to decode PKCS8 with EC private key: %s", err)
} }
} }

View File

@ -0,0 +1,69 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
"crypto/ecdsa"
"crypto/elliptic"
"encoding/asn1"
"errors"
"fmt"
"math/big"
)
const ecPrivKeyVersion = 1
// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
// References:
// RFC5915
// SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf
// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
// most cases it is not.
type ecPrivateKey struct {
Version int
PrivateKey []byte
NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
}
// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
return parseECPrivateKey(nil, der)
}
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
// The OID for the named curve may be provided from another source (such as
// the PKCS8 container) - if it is provided then use this instead of the OID
// that may exist in the EC private key structure.
func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
var privKey ecPrivateKey
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
return nil, errors.New("crypto/x509: failed to parse EC private key: " + err.Error())
}
if privKey.Version != ecPrivKeyVersion {
return nil, fmt.Errorf("crypto/x509: unknown EC private key version %d", privKey.Version)
}
var curve elliptic.Curve
if namedCurveOID != nil {
curve = namedCurveFromOID(*namedCurveOID)
} else {
curve = namedCurveFromOID(privKey.NamedCurveOID)
}
if curve == nil {
return nil, errors.New("crypto/x509: unknown elliptic curve")
}
k := new(big.Int).SetBytes(privKey.PrivateKey)
if k.Cmp(curve.Params().N) >= 0 {
return nil, errors.New("crypto/x509: invalid elliptic curve private key value")
}
priv := new(ecdsa.PrivateKey)
priv.Curve = curve
priv.D = k
priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey)
return priv, nil
}

View File

@ -0,0 +1,22 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
"encoding/hex"
"testing"
)
// Generated using:
// openssl ecparam -genkey -name secp384r1 -outform PEM
var ecPrivateKeyHex = `3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50`
func TestParseECPrivateKey(t *testing.T) {
derBytes, _ := hex.DecodeString(ecPrivateKeyHex)
_, err := ParseECPrivateKey(derBytes)
if err != nil {
t.Errorf("failed to decode EC private key: %s", err)
}
}

View File

@ -156,8 +156,8 @@ const (
// //
// pkcs-1 OBJECT IDENTIFIER ::= { // pkcs-1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
// //
// //
// RFC 3279 2.2.1 RSA Signature Algorithms // RFC 3279 2.2.1 RSA Signature Algorithms
// //
// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 } // md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
@ -165,19 +165,19 @@ const (
// md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 } // md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
// //
// sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } // sha-1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
// //
// dsaWithSha1 OBJECT IDENTIFIER ::= { // dsaWithSha1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } // iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }
// //
// RFC 3279 2.2.3 ECDSA Signature Algorithm // RFC 3279 2.2.3 ECDSA Signature Algorithm
// //
// ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { // ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) ansi-x962(10045) // iso(1) member-body(2) us(840) ansi-x962(10045)
// signatures(4) ecdsa-with-SHA1(1)} // signatures(4) ecdsa-with-SHA1(1)}
// //
// //
// RFC 4055 5 PKCS #1 Version 1.5 // RFC 4055 5 PKCS #1 Version 1.5
// //
// sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } // sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 }
// //
// sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } // sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 }
@ -1224,7 +1224,7 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub interf
SerialNumber: template.SerialNumber, SerialNumber: template.SerialNumber,
SignatureAlgorithm: signatureAlgorithm, SignatureAlgorithm: signatureAlgorithm,
Issuer: asn1.RawValue{FullBytes: asn1Issuer}, Issuer: asn1.RawValue{FullBytes: asn1Issuer},
Validity: validity{template.NotBefore, template.NotAfter}, Validity: validity{template.NotBefore.UTC(), template.NotAfter.UTC()},
Subject: asn1.RawValue{FullBytes: asn1Subject}, Subject: asn1.RawValue{FullBytes: asn1Subject},
PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey}, PublicKey: publicKeyInfo{nil, publicKeyAlgorithm, encodedPublicKey},
Extensions: extensions, Extensions: extensions,
@ -1314,8 +1314,8 @@ func (c *Certificate) CreateCRL(rand io.Reader, priv interface{}, revokedCerts [
Algorithm: oidSignatureSHA1WithRSA, Algorithm: oidSignatureSHA1WithRSA,
}, },
Issuer: c.Subject.ToRDNSequence(), Issuer: c.Subject.ToRDNSequence(),
ThisUpdate: now, ThisUpdate: now.UTC(),
NextUpdate: expiry, NextUpdate: expiry.UTC(),
RevokedCertificates: revokedCerts, RevokedCertificates: revokedCerts,
} }

View File

@ -417,10 +417,6 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
return nil, nil, errors.New("cannot load string table section") return nil, nil, errors.New("cannot load string table section")
} }
// The first entry is all zeros.
var skip [Sym32Size]byte
symtab.Read(skip[0:])
symbols := make([]Symbol, symtab.Len()/Sym32Size) symbols := make([]Symbol, symtab.Len()/Sym32Size)
i := 0 i := 0
@ -460,10 +456,6 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
return nil, nil, errors.New("cannot load string table section") return nil, nil, errors.New("cannot load string table section")
} }
// The first entry is all zeros.
var skip [Sym64Size]byte
symtab.Read(skip[0:])
symbols := make([]Symbol, symtab.Len()/Sym64Size) symbols := make([]Symbol, symtab.Len()/Sym64Size)
i := 0 i := 0
@ -708,8 +700,8 @@ func (f *File) gnuVersionInit(str []byte) {
// gnuVersion adds Library and Version information to sym, // gnuVersion adds Library and Version information to sym,
// which came from offset i of the symbol table. // which came from offset i of the symbol table.
func (f *File) gnuVersion(i int, sym *ImportedSymbol) { func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
// Each entry is two bytes; skip undef entry at beginning. // Each entry is two bytes.
i = (i + 1) * 2 i = i * 2
if i >= len(f.gnuVersym) { if i >= len(f.gnuVersym) {
return return
} }

View File

@ -175,23 +175,41 @@ func TestOpen(t *testing.T) {
} }
} }
type relocationTestEntry struct {
entryNumber int
entry *dwarf.Entry
}
type relocationTest struct { type relocationTest struct {
file string file string
firstEntry *dwarf.Entry entries []relocationTestEntry
} }
var relocationTests = []relocationTest{ var relocationTests = []relocationTest{
{ {
"testdata/go-relocation-test-gcc441-x86-64.obj", "testdata/go-relocation-test-gcc441-x86-64.obj",
&dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, []relocationTestEntry{
{0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
},
}, },
{ {
"testdata/go-relocation-test-gcc441-x86.obj", "testdata/go-relocation-test-gcc441-x86.obj",
&dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, []relocationTestEntry{
{0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.4.1"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "t.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x5)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
},
}, },
{ {
"testdata/go-relocation-test-gcc424-x86-64.obj", "testdata/go-relocation-test-gcc424-x86-64.obj",
&dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}, []relocationTestEntry{
{0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
},
},
{
"testdata/gcc-amd64-openbsd-debug-with-rela.obj",
[]relocationTestEntry{
{203, &dwarf.Entry{Offset: 0xc62, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_interval"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(236)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x0}}}}},
{204, &dwarf.Entry{Offset: 0xc70, Tag: dwarf.TagMember, Children: false, Field: []dwarf.Field{{Attr: dwarf.AttrName, Val: "it_value"}, {Attr: dwarf.AttrDeclFile, Val: int64(7)}, {Attr: dwarf.AttrDeclLine, Val: int64(237)}, {Attr: dwarf.AttrType, Val: dwarf.Offset(0xb7f)}, {Attr: dwarf.AttrDataMemberLoc, Val: []byte{0x23, 0x10}}}}},
},
}, },
} }
@ -207,20 +225,24 @@ func TestDWARFRelocations(t *testing.T) {
t.Error(err) t.Error(err)
continue continue
} }
reader := dwarf.Reader() for _, testEntry := range test.entries {
// Checking only the first entry is sufficient since it has reader := dwarf.Reader()
// many different strings. If the relocation had failed, all for j := 0; j < testEntry.entryNumber; j++ {
// the string offsets would be zero and all the strings would entry, err := reader.Next()
// end up being the same. if entry == nil || err != nil {
firstEntry, err := reader.Next() t.Errorf("Failed to skip to entry %d: %v", testEntry.entryNumber, err)
if err != nil { continue
t.Error(err) }
continue }
} entry, err := reader.Next()
if err != nil {
if !reflect.DeepEqual(test.firstEntry, firstEntry) { t.Error(err)
t.Errorf("#%d: mismatch: got:%#v want:%#v", i, firstEntry, test.firstEntry) continue
continue }
if !reflect.DeepEqual(testEntry.entry, entry) {
t.Errorf("#%d/%d: mismatch: got:%#v want:%#v", i, testEntry.entryNumber, entry, testEntry.entry)
continue
}
} }
} }
} }

Binary file not shown.

View File

@ -296,8 +296,7 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) {
} }
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) { func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
utc := t.UTC() year, month, day := t.Date()
year, month, day := utc.Date()
switch { switch {
case 1950 <= year && year < 2000: case 1950 <= year && year < 2000:
@ -321,7 +320,7 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
return return
} }
hour, min, sec := utc.Clock() hour, min, sec := t.Clock()
err = marshalTwoDigits(out, hour) err = marshalTwoDigits(out, hour)
if err != nil { if err != nil {

View File

@ -82,7 +82,7 @@ var marshalTests = []marshalTest{
{explicitTagTest{64}, "3005a503020140"}, {explicitTagTest{64}, "3005a503020140"},
{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"}, {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"}, {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
{time.Unix(1258325776, 0).In(PST), "17113039313131353232353631362d30383030"}, {time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
{BitString{[]byte{0x80}, 1}, "03020780"}, {BitString{[]byte{0x80}, 1}, "03020780"},
{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"}, {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"}, {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},

View File

@ -125,6 +125,9 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
// of fixed-size values. // of fixed-size values.
// Bytes read from r are decoded using the specified byte order // Bytes read from r are decoded using the specified byte order
// and written to successive fields of the data. // and written to successive fields of the data.
// When reading into structs, the field data for fields with
// blank (_) field names is skipped; i.e., blank field names
// may be used for padding.
func Read(r io.Reader, order ByteOrder, data interface{}) error { func Read(r io.Reader, order ByteOrder, data interface{}) error {
// Fast path for basic types. // Fast path for basic types.
if n := intDestSize(data); n != 0 { if n := intDestSize(data); n != 0 {
@ -154,7 +157,7 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
return nil return nil
} }
// Fallback to reflect-based. // Fallback to reflect-based decoding.
var v reflect.Value var v reflect.Value
switch d := reflect.ValueOf(data); d.Kind() { switch d := reflect.ValueOf(data); d.Kind() {
case reflect.Ptr: case reflect.Ptr:
@ -181,6 +184,8 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
// values, or a pointer to such data. // values, or a pointer to such data.
// Bytes written to w are encoded using the specified byte order // Bytes written to w are encoded using the specified byte order
// and read from successive fields of the data. // and read from successive fields of the data.
// When writing structs, zero values are are written for fields
// with blank (_) field names.
func Write(w io.Writer, order ByteOrder, data interface{}) error { func Write(w io.Writer, order ByteOrder, data interface{}) error {
// Fast path for basic types. // Fast path for basic types.
var b [8]byte var b [8]byte
@ -239,6 +244,8 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error {
_, err := w.Write(bs) _, err := w.Write(bs)
return err return err
} }
// Fallback to reflect-based encoding.
v := reflect.Indirect(reflect.ValueOf(data)) v := reflect.Indirect(reflect.ValueOf(data))
size := dataSize(v) size := dataSize(v)
if size < 0 { if size < 0 {
@ -300,15 +307,13 @@ func sizeof(t reflect.Type) int {
return -1 return -1
} }
type decoder struct { type coder struct {
order ByteOrder order ByteOrder
buf []byte buf []byte
} }
type encoder struct { type decoder coder
order ByteOrder type encoder coder
buf []byte
}
func (d *decoder) uint8() uint8 { func (d *decoder) uint8() uint8 {
x := d.buf[0] x := d.buf[0]
@ -379,9 +384,19 @@ func (d *decoder) value(v reflect.Value) {
} }
case reflect.Struct: case reflect.Struct:
t := v.Type()
l := v.NumField() l := v.NumField()
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
d.value(v.Field(i)) // Note: Calling v.CanSet() below is an optimization.
// It would be sufficient to check the field name,
// but creating the StructField info for each field is
// costly (run "go test -bench=ReadStruct" and compare
// results when making changes to this code).
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
d.value(v)
} else {
d.skip(v)
}
} }
case reflect.Slice: case reflect.Slice:
@ -435,9 +450,15 @@ func (e *encoder) value(v reflect.Value) {
} }
case reflect.Struct: case reflect.Struct:
t := v.Type()
l := v.NumField() l := v.NumField()
for i := 0; i < l; i++ { for i := 0; i < l; i++ {
e.value(v.Field(i)) // see comment for corresponding code in decoder.value()
if v := v.Field(i); v.CanSet() || t.Field(i).Name != "_" {
e.value(v)
} else {
e.skip(v)
}
} }
case reflect.Slice: case reflect.Slice:
@ -492,6 +513,18 @@ func (e *encoder) value(v reflect.Value) {
} }
} }
func (d *decoder) skip(v reflect.Value) {
d.buf = d.buf[dataSize(v):]
}
func (e *encoder) skip(v reflect.Value) {
n := dataSize(v)
for i := range e.buf[0:n] {
e.buf[i] = 0
}
e.buf = e.buf[n:]
}
// intDestSize returns the size of the integer that ptrType points to, // intDestSize returns the size of the integer that ptrType points to,
// or 0 if the type is not supported. // or 0 if the type is not supported.
func intDestSize(ptrType interface{}) int { func intDestSize(ptrType interface{}) int {

View File

@ -121,18 +121,14 @@ func testWrite(t *testing.T, order ByteOrder, b []byte, s1 interface{}) {
checkResult(t, "Write", order, err, buf.Bytes(), b) checkResult(t, "Write", order, err, buf.Bytes(), b)
} }
func TestBigEndianRead(t *testing.T) { testRead(t, BigEndian, big, s) } func TestLittleEndianRead(t *testing.T) { testRead(t, LittleEndian, little, s) }
func TestLittleEndianWrite(t *testing.T) { testWrite(t, LittleEndian, little, s) }
func TestLittleEndianRead(t *testing.T) { testRead(t, LittleEndian, little, s) }
func TestBigEndianWrite(t *testing.T) { testWrite(t, BigEndian, big, s) }
func TestLittleEndianWrite(t *testing.T) { testWrite(t, LittleEndian, little, s) }
func TestBigEndianPtrWrite(t *testing.T) { testWrite(t, BigEndian, big, &s) }
func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) } func TestLittleEndianPtrWrite(t *testing.T) { testWrite(t, LittleEndian, little, &s) }
func TestBigEndianRead(t *testing.T) { testRead(t, BigEndian, big, s) }
func TestBigEndianWrite(t *testing.T) { testWrite(t, BigEndian, big, s) }
func TestBigEndianPtrWrite(t *testing.T) { testWrite(t, BigEndian, big, &s) }
func TestReadSlice(t *testing.T) { func TestReadSlice(t *testing.T) {
slice := make([]int32, 2) slice := make([]int32, 2)
err := Read(bytes.NewBuffer(src), BigEndian, slice) err := Read(bytes.NewBuffer(src), BigEndian, slice)
@ -148,20 +144,75 @@ func TestWriteSlice(t *testing.T) {
func TestWriteT(t *testing.T) { func TestWriteT(t *testing.T) {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
ts := T{} ts := T{}
err := Write(buf, BigEndian, ts) if err := Write(buf, BigEndian, ts); err == nil {
if err == nil { t.Errorf("WriteT: have err == nil, want non-nil")
t.Errorf("WriteT: have nil, want non-nil")
} }
tv := reflect.Indirect(reflect.ValueOf(ts)) tv := reflect.Indirect(reflect.ValueOf(ts))
for i, n := 0, tv.NumField(); i < n; i++ { for i, n := 0, tv.NumField(); i < n; i++ {
err = Write(buf, BigEndian, tv.Field(i).Interface()) if err := Write(buf, BigEndian, tv.Field(i).Interface()); err == nil {
if err == nil { t.Errorf("WriteT.%v: have err == nil, want non-nil", tv.Field(i).Type())
t.Errorf("WriteT.%v: have nil, want non-nil", tv.Field(i).Type())
} }
} }
} }
type BlankFields struct {
A uint32
_ int32
B float64
_ [4]int16
C byte
_ [7]byte
_ struct {
f [8]float32
}
}
type BlankFieldsProbe struct {
A uint32
P0 int32
B float64
P1 [4]int16
C byte
P2 [7]byte
P3 struct {
F [8]float32
}
}
func TestBlankFields(t *testing.T) {
buf := new(bytes.Buffer)
b1 := BlankFields{A: 1234567890, B: 2.718281828, C: 42}
if err := Write(buf, LittleEndian, &b1); err != nil {
t.Error(err)
}
// zero values must have been written for blank fields
var p BlankFieldsProbe
if err := Read(buf, LittleEndian, &p); err != nil {
t.Error(err)
}
// quick test: only check first value of slices
if p.P0 != 0 || p.P1[0] != 0 || p.P2[0] != 0 || p.P3.F[0] != 0 {
t.Errorf("non-zero values for originally blank fields: %#v", p)
}
// write p and see if we can probe only some fields
if err := Write(buf, LittleEndian, &p); err != nil {
t.Error(err)
}
// read should ignore blank fields in b2
var b2 BlankFields
if err := Read(buf, LittleEndian, &b2); err != nil {
t.Error(err)
}
if b1.A != b2.A || b1.B != b2.B || b1.C != b2.C {
t.Errorf("%#v != %#v", b1, b2)
}
}
type byteSliceReader struct { type byteSliceReader struct {
remain []byte remain []byte
} }

View File

@ -123,7 +123,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) {
panic("unreachable") panic("unreachable")
} }
// ReadVarint reads an encoded unsigned integer from r and returns it as an int64. // ReadVarint reads an encoded signed integer from r and returns it as an int64.
func ReadVarint(r io.ByteReader) (int64, error) { func ReadVarint(r io.ByteReader) (int64, error) {
ux, err := ReadUvarint(r) // ok to continue in presence of error ux, err := ReadUvarint(r) // ok to continue in presence of error
x := int64(ux >> 1) x := int64(ux >> 1)

View File

@ -132,7 +132,7 @@ func (enc *Encoder) sendActualType(w io.Writer, state *encoderState, ut *userTyp
return true return true
} }
// sendType sends the type info to the other side, if necessary. // sendType sends the type info to the other side, if necessary.
func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) { func (enc *Encoder) sendType(w io.Writer, state *encoderState, origt reflect.Type) (sent bool) {
ut := userType(origt) ut := userType(origt)
if ut.isGobEncoder { if ut.isGobEncoder {

View File

@ -67,8 +67,8 @@ func Unmarshal(data []byte, v interface{}) error {
// Unmarshaler is the interface implemented by objects // Unmarshaler is the interface implemented by objects
// that can unmarshal a JSON description of themselves. // that can unmarshal a JSON description of themselves.
// The input can be assumed to be a valid JSON object // The input can be assumed to be a valid encoding of
// encoding. UnmarshalJSON must copy the JSON data // a JSON value. UnmarshalJSON must copy the JSON data
// if it wishes to retain the data after returning. // if it wishes to retain the data after returning.
type Unmarshaler interface { type Unmarshaler interface {
UnmarshalJSON([]byte) error UnmarshalJSON([]byte) error
@ -617,12 +617,10 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
switch c := item[0]; c { switch c := item[0]; c {
case 'n': // null case 'n': // null
switch v.Kind() { switch v.Kind() {
default:
d.saveError(&UnmarshalTypeError{"null", v.Type()})
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
v.Set(reflect.Zero(v.Type())) v.Set(reflect.Zero(v.Type()))
// otherwise, ignore null for primitives/string
} }
case 't', 'f': // true, false case 't', 'f': // true, false
value := c == 't' value := c == 't'
switch v.Kind() { switch v.Kind() {

View File

@ -953,3 +953,50 @@ func TestInterfaceSet(t *testing.T) {
} }
} }
} }
// JSON null values should be ignored for primitives and string values instead of resulting in an error.
// Issue 2540
func TestUnmarshalNulls(t *testing.T) {
jsonData := []byte(`{
"Bool" : null,
"Int" : null,
"Int8" : null,
"Int16" : null,
"Int32" : null,
"Int64" : null,
"Uint" : null,
"Uint8" : null,
"Uint16" : null,
"Uint32" : null,
"Uint64" : null,
"Float32" : null,
"Float64" : null,
"String" : null}`)
nulls := All{
Bool: true,
Int: 2,
Int8: 3,
Int16: 4,
Int32: 5,
Int64: 6,
Uint: 7,
Uint8: 8,
Uint16: 9,
Uint32: 10,
Uint64: 11,
Float32: 12.1,
Float64: 13.1,
String: "14"}
err := Unmarshal(jsonData, &nulls)
if err != nil {
t.Errorf("Unmarshal of null values failed: %v", err)
}
if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
t.Errorf("Unmarshal of null values affected primitives")
}
}

View File

@ -86,7 +86,7 @@ import (
// //
// Anonymous struct fields are usually marshaled as if their inner exported fields // Anonymous struct fields are usually marshaled as if their inner exported fields
// were fields in the outer struct, subject to the usual Go visibility rules. // were fields in the outer struct, subject to the usual Go visibility rules.
// An anonymous struct field with a name given in its JSON tag is treated as // An anonymous struct field with a name given in its JSON tag is treated as
// having that name instead of as anonymous. // having that name instead of as anonymous.
// //
// Handling of anonymous struct fields is new in Go 1.1. // Handling of anonymous struct fields is new in Go 1.1.

View File

@ -11,6 +11,7 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"io" "io"
"sort"
) )
// A Block represents a PEM encoded structure. // A Block represents a PEM encoded structure.
@ -209,26 +210,46 @@ func (l *lineBreaker) Close() (err error) {
return return
} }
func Encode(out io.Writer, b *Block) (err error) { func writeHeader(out io.Writer, k, v string) error {
_, err = out.Write(pemStart[1:]) _, err := out.Write([]byte(k + ": " + v + "\n"))
if err != nil { return err
return }
func Encode(out io.Writer, b *Block) error {
if _, err := out.Write(pemStart[1:]); err != nil {
return err
} }
_, err = out.Write([]byte(b.Type + "-----\n")) if _, err := out.Write([]byte(b.Type + "-----\n")); err != nil {
if err != nil { return err
return
} }
if len(b.Headers) > 0 { if len(b.Headers) > 0 {
for k, v := range b.Headers { const procType = "Proc-Type"
_, err = out.Write([]byte(k + ": " + v + "\n")) h := make([]string, 0, len(b.Headers))
if err != nil { hasProcType := false
return for k := range b.Headers {
if k == procType {
hasProcType = true
continue
}
h = append(h, k)
}
// The Proc-Type header must be written first.
// See RFC 1421, section 4.6.1.1
if hasProcType {
if err := writeHeader(out, procType, b.Headers[procType]); err != nil {
return err
} }
} }
_, err = out.Write([]byte{'\n'}) // For consistency of output, write other headers sorted by key.
if err != nil { sort.Strings(h)
return for _, k := range h {
if err := writeHeader(out, k, b.Headers[k]); err != nil {
return err
}
}
if _, err := out.Write([]byte{'\n'}); err != nil {
return err
} }
} }
@ -236,19 +257,17 @@ func Encode(out io.Writer, b *Block) (err error) {
breaker.out = out breaker.out = out
b64 := base64.NewEncoder(base64.StdEncoding, &breaker) b64 := base64.NewEncoder(base64.StdEncoding, &breaker)
_, err = b64.Write(b.Bytes) if _, err := b64.Write(b.Bytes); err != nil {
if err != nil { return err
return
} }
b64.Close() b64.Close()
breaker.Close() breaker.Close()
_, err = out.Write(pemEnd[1:]) if _, err := out.Write(pemEnd[1:]); err != nil {
if err != nil { return err
return
} }
_, err = out.Write([]byte(b.Type + "-----\n")) _, err := out.Write([]byte(b.Type + "-----\n"))
return return err
} }
func EncodeToMemory(b *Block) []byte { func EncodeToMemory(b *Block) []byte {

View File

@ -43,7 +43,7 @@ func TestDecode(t *testing.T) {
if !reflect.DeepEqual(result, privateKey) { if !reflect.DeepEqual(result, privateKey) {
t.Errorf("#1 got:%#v want:%#v", result, privateKey) t.Errorf("#1 got:%#v want:%#v", result, privateKey)
} }
result, _ = Decode([]byte(pemPrivateKey)) result, _ = Decode([]byte(pemPrivateKey2))
if !reflect.DeepEqual(result, privateKey2) { if !reflect.DeepEqual(result, privateKey2) {
t.Errorf("#2 got:%#v want:%#v", result, privateKey2) t.Errorf("#2 got:%#v want:%#v", result, privateKey2)
} }
@ -51,8 +51,8 @@ func TestDecode(t *testing.T) {
func TestEncode(t *testing.T) { func TestEncode(t *testing.T) {
r := EncodeToMemory(privateKey2) r := EncodeToMemory(privateKey2)
if string(r) != pemPrivateKey { if string(r) != pemPrivateKey2 {
t.Errorf("got:%s want:%s", r, pemPrivateKey) t.Errorf("got:%s want:%s", r, pemPrivateKey2)
} }
} }
@ -341,50 +341,64 @@ var privateKey = &Block{Type: "RSA PRIVATE KEY",
}, },
} }
var privateKey2 = &Block{Type: "RSA PRIVATE KEY", var privateKey2 = &Block{
Headers: map[string]string{}, Type: "RSA PRIVATE KEY",
Bytes: []uint8{0x30, 0x82, 0x1, 0x3a, 0x2, 0x1, 0x0, 0x2, Headers: map[string]string{
0x41, 0x0, 0xb2, 0x99, 0xf, 0x49, 0xc4, 0x7d, 0xfa, 0x8c, "Proc-Type": "4,ENCRYPTED",
0xd4, 0x0, 0xae, 0x6a, 0x4d, 0x1b, 0x8a, 0x3b, 0x6a, 0x13, "DEK-Info": "AES-128-CBC,BFCD243FEDBB40A4AA6DDAA1335473A4",
0x64, 0x2b, 0x23, 0xf2, 0x8b, 0x0, 0x3b, 0xfb, 0x97, 0x79, "Content-Domain": "RFC822",
0xa, 0xde, 0x9a, 0x4c, 0xc8, 0x2b, 0x8b, 0x2a, 0x81, 0x74, },
0x7d, 0xde, 0xc0, 0x8b, 0x62, 0x96, 0xe5, 0x3a, 0x8, 0xc3, Bytes: []uint8{
0x31, 0x68, 0x7e, 0xf2, 0x5c, 0x4b, 0xf4, 0x93, 0x6b, 0xa1, 0xa8, 0x35, 0xcc, 0x2b, 0xb9, 0xcb, 0x21, 0xab, 0xc0,
0xc0, 0xe6, 0x4, 0x1e, 0x9d, 0x15, 0x2, 0x3, 0x1, 0x0, 0x1, 0x9d, 0x76, 0x61, 0x0, 0xf4, 0x81, 0xad, 0x69, 0xd2,
0x2, 0x41, 0x0, 0x8a, 0xbd, 0x6a, 0x69, 0xf4, 0xd1, 0xa4, 0xc0, 0x42, 0x41, 0x3b, 0xe4, 0x3c, 0xaf, 0x59, 0x5e,
0xb4, 0x87, 0xf0, 0xab, 0x8d, 0x7a, 0xae, 0xfd, 0x38, 0x60, 0x6d, 0x2a, 0x3c, 0x9c, 0xa1, 0xa4, 0x5e, 0x68, 0x37,
0x94, 0x5, 0xc9, 0x99, 0x98, 0x4e, 0x30, 0xf5, 0x67, 0xe1, 0xc4, 0x8c, 0x70, 0x1c, 0xa9, 0x18, 0xe6, 0xc2, 0x2b,
0xe8, 0xae, 0xef, 0xf4, 0x4e, 0x8b, 0x18, 0xbd, 0xb1, 0xec, 0x8a, 0x91, 0xdc, 0x2d, 0x1f, 0x8, 0x23, 0x39, 0xf1,
0x78, 0xdf, 0xa3, 0x1a, 0x55, 0xe3, 0x2a, 0x48, 0xd7, 0xfb, 0x4b, 0x8b, 0x1b, 0x2f, 0x46, 0xb, 0xb2, 0x26, 0xba,
0x13, 0x1f, 0x5a, 0xf1, 0xf4, 0x4d, 0x7d, 0x6b, 0x2c, 0xed, 0x4f, 0x40, 0x80, 0x39, 0xc4, 0xb1, 0xcb, 0x3b, 0xb4,
0x2a, 0x9d, 0xf5, 0xe5, 0xae, 0x45, 0x35, 0x2, 0x21, 0x0, 0x65, 0x3f, 0x1b, 0xb2, 0xf7, 0x8, 0xd2, 0xc6, 0xd5,
0xda, 0xb2, 0xf1, 0x80, 0x48, 0xba, 0xa6, 0x8d, 0xe7, 0xdf, 0xa8, 0x9f, 0x23, 0x69, 0xb6, 0x3d, 0xf9, 0xac, 0x1c,
0x4, 0xd2, 0xd3, 0x5d, 0x5d, 0x80, 0xe6, 0xe, 0x2d, 0xfa, 0xb3, 0x13, 0x87, 0x64, 0x4, 0x37, 0xdb, 0x40, 0xc8,
0x42, 0xd5, 0xa, 0x9b, 0x4, 0x21, 0x90, 0x32, 0x71, 0x5e, 0x82, 0xc, 0xd0, 0xf8, 0x21, 0x7c, 0xdc, 0xbd, 0x9, 0x4,
0x46, 0xb3, 0x2, 0x21, 0x0, 0xd1, 0xf, 0x2e, 0x66, 0xb1, 0x20, 0x16, 0xb0, 0x97, 0xe2, 0x6d, 0x56, 0x1d, 0xe3,
0xd0, 0xc1, 0x3f, 0x10, 0xef, 0x99, 0x27, 0xbf, 0x53, 0x24, 0xec, 0xf0, 0xfc, 0xe2, 0x56, 0xad, 0xa4, 0x3, 0x70,
0xa3, 0x79, 0xca, 0x21, 0x81, 0x46, 0xcb, 0xf9, 0xca, 0xfc, 0x6d, 0x63, 0x3c, 0x1, 0xbe, 0x3e, 0x28, 0x38, 0x6f,
0x79, 0x52, 0x21, 0xf1, 0x6a, 0x31, 0x17, 0x2, 0x20, 0x21, 0xc0, 0xe6, 0xfd, 0x85, 0xd1, 0x53, 0xa8, 0x9b, 0xcb,
0x2, 0x89, 0x79, 0x37, 0x81, 0x14, 0xca, 0xae, 0x88, 0xf7, 0xd4, 0x4, 0xb1, 0x73, 0xb9, 0x73, 0x32, 0xd6, 0x7a,
0xd, 0x6b, 0x61, 0xd8, 0x4f, 0x30, 0x6a, 0x4b, 0x7e, 0x4e, 0xc6, 0x29, 0x25, 0xa5, 0xda, 0x17, 0x93, 0x7a, 0x10,
0xc0, 0x21, 0x4d, 0xac, 0x9d, 0xf4, 0x49, 0xe8, 0xda, 0xb6, 0xe8, 0x41, 0xfb, 0xa5, 0x17, 0x20, 0xf8, 0x4e, 0xe9,
0x9, 0x2, 0x20, 0x16, 0xb3, 0xec, 0x59, 0x10, 0xa4, 0x57, 0xe3, 0x8f, 0x51, 0x20, 0x13, 0xbb, 0xde, 0xb7, 0x93,
0xe8, 0xe, 0x61, 0xc6, 0xa3, 0xf, 0x5e, 0xeb, 0x12, 0xa9, 0xae, 0x13, 0x8a, 0xf6, 0x9, 0xf4, 0xa6, 0x41, 0xe0,
0xae, 0x2e, 0xb7, 0x48, 0x45, 0xec, 0x69, 0x83, 0xc3, 0x75, 0x2b, 0x51, 0x1a, 0x30, 0x38, 0xd, 0xb1, 0x3b, 0x67,
0xc, 0xe4, 0x97, 0xa0, 0x9f, 0x2, 0x20, 0x69, 0x52, 0xb4, 0x87, 0x64, 0xf5, 0xca, 0x32, 0x67, 0xd1, 0xc8, 0xa5,
0x6, 0xe8, 0x50, 0x60, 0x71, 0x4c, 0x3a, 0xb7, 0x66, 0xba, 0x3d, 0x23, 0x72, 0xc4, 0x6, 0xaf, 0x8f, 0x7b, 0x26,
0xd, 0x8a, 0xc9, 0xb7, 0xd, 0xa3, 0x8, 0x6c, 0xa3, 0xf2, 0xac, 0x3c, 0x75, 0x91, 0xa1, 0x0, 0x13, 0xc6, 0x5c,
0x62, 0xb0, 0x2a, 0x84, 0xaa, 0x2f, 0xd6, 0x1e, 0x55, 0x49, 0xd5, 0x3c, 0xe7, 0xb2, 0xb2, 0x99, 0xe0, 0xd5,
0x25, 0xfa, 0xe2, 0x12, 0x80, 0x37, 0x85, 0xcf, 0x92,
0xca, 0x1b, 0x9f, 0xf3, 0x4e, 0xd8, 0x80, 0xef, 0x3c,
0xce, 0xcd, 0xf5, 0x90, 0x9e, 0xf9, 0xa7, 0xb2, 0xc,
0x49, 0x4, 0xf1, 0x9, 0x8f, 0xea, 0x63, 0xd2, 0x70,
0xbb, 0x86, 0xbf, 0x34, 0xab, 0xb2, 0x3, 0xb1, 0x59,
0x33, 0x16, 0x17, 0xb0, 0xdb, 0x77, 0x38, 0xf4, 0xb4,
0x94, 0xb, 0x25, 0x16, 0x7e, 0x22, 0xd4, 0xf9, 0x22,
0xb9, 0x78, 0xa3, 0x4, 0x84, 0x4, 0xd2, 0xda, 0x84,
0x2d, 0x63, 0xdd, 0xf8, 0x50, 0x6a, 0xf6, 0xe3, 0xf5,
0x65, 0x40, 0x7c, 0xa9,
}, },
} }
var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY----- var pemPrivateKey2 = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0 Proc-Type: 4,ENCRYPTED
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu Content-Domain: RFC822
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu DEK-Info: AES-128-CBC,BFCD243FEDBB40A4AA6DDAA1335473A4
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A qDXMK7nLIavAnXZhAPSBrWnSwEJBO+Q8r1lebSo8nKGkXmg3xIxwHKkY5sIripHc
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS LR8IIznxS4sbL0YLsia6T0CAOcSxyzu0ZT8bsvcI0sbVqJ8jabY9+awcsxOHZAQ3
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V 20DIggzQ+CF83L0JBCAWsJfibVYd4+zw/OJWraQDcG1jPAG+Pig4b8Dm/YXRU6ib
y9QEsXO5czLWesYpJaXaF5N6EOhB+6UXIPhO6eOPUSATu963k64TivYJ9KZB4CtR
GjA4DbE7Z4dk9coyZ9HIpT0jcsQGr497Jqw8dZGhABPGXEnVPOeyspng1SX64hKA
N4XPksobn/NO2IDvPM7N9ZCe+aeyDEkE8QmP6mPScLuGvzSrsgOxWTMWF7Dbdzj0
tJQLJRZ+ItT5Irl4owSEBNLahC1j3fhQavbj9WVAfKk=
-----END RSA PRIVATE KEY----- -----END RSA PRIVATE KEY-----
` `

View File

@ -687,6 +687,27 @@ var marshalTests = []struct {
Value: &IgnoreTest{}, Value: &IgnoreTest{},
UnmarshalOnly: true, UnmarshalOnly: true,
}, },
// Test escaping.
{
ExpectXML: `<a><nested><value>dquote: &#34;; squote: &#39;; ampersand: &amp;; less: &lt;; greater: &gt;;</value></nested></a>`,
Value: &AnyTest{
Nested: `dquote: "; squote: '; ampersand: &; less: <; greater: >;`,
},
},
{
ExpectXML: `<a><nested><value>newline: &#xA;; cr: &#xD;; tab: &#x9;;</value></nested></a>`,
Value: &AnyTest{
Nested: "newline: \n; cr: \r; tab: \t;",
},
},
{
ExpectXML: "<a><nested><value>1\r2\r\n3\n\r4\n5</value></nested></a>",
Value: &AnyTest{
Nested: "1\n2\n3\n\n4\n5",
},
UnmarshalOnly: true,
},
} }
func TestMarshal(t *testing.T) { func TestMarshal(t *testing.T) {

View File

@ -181,7 +181,6 @@ type Decoder struct {
ns map[string]string ns map[string]string
err error err error
line int line int
tmp [32]byte
} }
// NewDecoder creates a new XML parser reading from r. // NewDecoder creates a new XML parser reading from r.
@ -877,94 +876,103 @@ Input:
// XML in all its glory allows a document to define and use // XML in all its glory allows a document to define and use
// its own character names with <!ENTITY ...> directives. // its own character names with <!ENTITY ...> directives.
// Parsers are required to recognize lt, gt, amp, apos, and quot // Parsers are required to recognize lt, gt, amp, apos, and quot
// even if they have not been declared. That's all we allow. // even if they have not been declared.
var i int before := d.buf.Len()
var semicolon bool d.buf.WriteByte('&')
var valid bool var ok bool
for i = 0; i < len(d.tmp); i++ { var text string
var ok bool var haveText bool
d.tmp[i], ok = d.getc() if b, ok = d.mustgetc(); !ok {
if !ok { return nil
if d.err == io.EOF { }
d.err = d.syntaxError("unexpected EOF") if b == '#' {
} d.buf.WriteByte(b)
if b, ok = d.mustgetc(); !ok {
return nil return nil
} }
c := d.tmp[i] base := 10
if c == ';' { if b == 'x' {
semicolon = true base = 16
valid = i > 0 d.buf.WriteByte(b)
break if b, ok = d.mustgetc(); !ok {
} return nil
if 'a' <= c && c <= 'z' ||
'A' <= c && c <= 'Z' ||
'0' <= c && c <= '9' ||
c == '_' || c == '#' {
continue
}
d.ungetc(c)
break
}
s := string(d.tmp[0:i])
if !valid {
if !d.Strict {
b0, b1 = 0, 0
d.buf.WriteByte('&')
d.buf.Write(d.tmp[0:i])
if semicolon {
d.buf.WriteByte(';')
} }
continue Input
} }
semi := ";" start := d.buf.Len()
if !semicolon { for '0' <= b && b <= '9' ||
semi = " (no semicolon)" base == 16 && 'a' <= b && b <= 'f' ||
base == 16 && 'A' <= b && b <= 'F' {
d.buf.WriteByte(b)
if b, ok = d.mustgetc(); !ok {
return nil
}
} }
if i < len(d.tmp) { if b != ';' {
d.err = d.syntaxError("invalid character entity &" + s + semi) d.ungetc(b)
} else { } else {
d.err = d.syntaxError("invalid character entity &" + s + "... too long") s := string(d.buf.Bytes()[start:])
} d.buf.WriteByte(';')
return nil n, err := strconv.ParseUint(s, base, 64)
} if err == nil && n <= unicode.MaxRune {
var haveText bool text = string(n)
var text string haveText = true
if i >= 2 && s[0] == '#' { }
var n uint64
var err error
if i >= 3 && s[1] == 'x' {
n, err = strconv.ParseUint(s[2:], 16, 64)
} else {
n, err = strconv.ParseUint(s[1:], 10, 64)
}
if err == nil && n <= unicode.MaxRune {
text = string(n)
haveText = true
} }
} else { } else {
if r, ok := entity[s]; ok { d.ungetc(b)
text = string(r) if !d.readName() {
haveText = true if d.err != nil {
} else if d.Entity != nil { return nil
text, haveText = d.Entity[s] }
ok = false
} }
} if b, ok = d.mustgetc(); !ok {
if !haveText { return nil
if !d.Strict { }
b0, b1 = 0, 0 if b != ';' {
d.buf.WriteByte('&') d.ungetc(b)
d.buf.Write(d.tmp[0:i]) } else {
name := d.buf.Bytes()[before+1:]
d.buf.WriteByte(';') d.buf.WriteByte(';')
continue Input if isName(name) {
s := string(name)
if r, ok := entity[s]; ok {
text = string(r)
haveText = true
} else if d.Entity != nil {
text, haveText = d.Entity[s]
}
}
} }
d.err = d.syntaxError("invalid character entity &" + s + ";")
return nil
} }
d.buf.Write([]byte(text))
b0, b1 = 0, 0 if haveText {
continue Input d.buf.Truncate(before)
d.buf.Write([]byte(text))
b0, b1 = 0, 0
continue Input
}
if !d.Strict {
b0, b1 = 0, 0
continue Input
}
ent := string(d.buf.Bytes()[before])
if ent[len(ent)-1] != ';' {
ent += " (no semicolon)"
}
d.err = d.syntaxError("invalid character entity " + ent)
return nil
} }
d.buf.WriteByte(b)
// We must rewrite unescaped \r and \r\n into \n.
if b == '\r' {
d.buf.WriteByte('\n')
} else if b1 == '\r' && b == '\n' {
// Skip \r\n--we already wrote \n.
} else {
d.buf.WriteByte(b)
}
b0, b1 = b1, b b0, b1 = b1, b
} }
data := d.buf.Bytes() data := d.buf.Bytes()
@ -985,20 +993,7 @@ Input:
} }
} }
// Must rewrite \r and \r\n into \n. return data
w := 0
for r := 0; r < len(data); r++ {
b := data[r]
if b == '\r' {
if r+1 < len(data) && data[r+1] == '\n' {
continue
}
b = '\n'
}
data[w] = b
w++
}
return data[0:w]
} }
// Decide whether the given rune is in the XML Character Range, per // Decide whether the given rune is in the XML Character Range, per
@ -1034,18 +1029,34 @@ func (d *Decoder) nsname() (name Name, ok bool) {
// Do not set d.err if the name is missing (unless unexpected EOF is received): // Do not set d.err if the name is missing (unless unexpected EOF is received):
// let the caller provide better context. // let the caller provide better context.
func (d *Decoder) name() (s string, ok bool) { func (d *Decoder) name() (s string, ok bool) {
d.buf.Reset()
if !d.readName() {
return "", false
}
// Now we check the characters.
s = d.buf.String()
if !isName([]byte(s)) {
d.err = d.syntaxError("invalid XML name: " + s)
return "", false
}
return s, true
}
// Read a name and append its bytes to d.buf.
// The name is delimited by any single-byte character not valid in names.
// All multi-byte characters are accepted; the caller must check their validity.
func (d *Decoder) readName() (ok bool) {
var b byte var b byte
if b, ok = d.mustgetc(); !ok { if b, ok = d.mustgetc(); !ok {
return return
} }
// As a first approximation, we gather the bytes [A-Za-z_:.-\x80-\xFF]*
if b < utf8.RuneSelf && !isNameByte(b) { if b < utf8.RuneSelf && !isNameByte(b) {
d.ungetc(b) d.ungetc(b)
return "", false return false
} }
d.buf.Reset()
d.buf.WriteByte(b) d.buf.WriteByte(b)
for { for {
if b, ok = d.mustgetc(); !ok { if b, ok = d.mustgetc(); !ok {
return return
@ -1056,16 +1067,7 @@ func (d *Decoder) name() (s string, ok bool) {
} }
d.buf.WriteByte(b) d.buf.WriteByte(b)
} }
return true
// Then we check the characters.
s = d.buf.String()
for i, c := range s {
if !unicode.Is(first, c) && (i == 0 || !unicode.Is(second, c)) {
d.err = d.syntaxError("invalid XML name: " + s)
return "", false
}
}
return s, true
} }
func isNameByte(c byte) bool { func isNameByte(c byte) bool {
@ -1075,6 +1077,30 @@ func isNameByte(c byte) bool {
c == '_' || c == ':' || c == '.' || c == '-' c == '_' || c == ':' || c == '.' || c == '-'
} }
func isName(s []byte) bool {
if len(s) == 0 {
return false
}
c, n := utf8.DecodeRune(s)
if c == utf8.RuneError && n == 1 {
return false
}
if !unicode.Is(first, c) {
return false
}
for n < len(s) {
s = s[n:]
c, n = utf8.DecodeRune(s)
if c == utf8.RuneError && n == 1 {
return false
}
if !unicode.Is(first, c) && !unicode.Is(second, c) {
return false
}
}
return true
}
// These tables were generated by cut and paste from Appendix B of // These tables were generated by cut and paste from Appendix B of
// the XML spec at http://www.xml.com/axml/testaxml.htm // the XML spec at http://www.xml.com/axml/testaxml.htm
// and then reformatting. First corresponds to (Letter | '_' | ':') // and then reformatting. First corresponds to (Letter | '_' | ':')
@ -1689,6 +1715,9 @@ var (
esc_amp = []byte("&amp;") esc_amp = []byte("&amp;")
esc_lt = []byte("&lt;") esc_lt = []byte("&lt;")
esc_gt = []byte("&gt;") esc_gt = []byte("&gt;")
esc_tab = []byte("&#x9;")
esc_nl = []byte("&#xA;")
esc_cr = []byte("&#xD;")
) )
// Escape writes to w the properly escaped XML equivalent // Escape writes to w the properly escaped XML equivalent
@ -1708,6 +1737,12 @@ func Escape(w io.Writer, s []byte) {
esc = esc_lt esc = esc_lt
case '>': case '>':
esc = esc_gt esc = esc_gt
case '\t':
esc = esc_tab
case '\n':
esc = esc_nl
case '\r':
esc = esc_cr
default: default:
continue continue
} }

View File

@ -19,6 +19,7 @@ const testInput = `
<body xmlns:foo="ns1" xmlns="ns2" xmlns:tag="ns3" ` + <body xmlns:foo="ns1" xmlns="ns2" xmlns:tag="ns3" ` +
"\r\n\t" + ` > "\r\n\t" + ` >
<hello lang="en">World &lt;&gt;&apos;&quot; &#x767d;&#40300;</hello> <hello lang="en">World &lt;&gt;&apos;&quot; &#x767d;&#40300;</hello>
<query>&; &is-it;</query>
<goodbye /> <goodbye />
<outer foo:attr="value" xmlns:tag="ns4"> <outer foo:attr="value" xmlns:tag="ns4">
<inner/> <inner/>
@ -28,6 +29,8 @@ const testInput = `
</tag:name> </tag:name>
</body><!-- missing final newline -->` </body><!-- missing final newline -->`
var testEntity = map[string]string{"何": "What", "is-it": "is it?"}
var rawTokens = []Token{ var rawTokens = []Token{
CharData("\n"), CharData("\n"),
ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)}, ProcInst{"xml", []byte(`version="1.0" encoding="UTF-8"`)},
@ -41,6 +44,10 @@ var rawTokens = []Token{
CharData("World <>'\" 白鵬翔"), CharData("World <>'\" 白鵬翔"),
EndElement{Name{"", "hello"}}, EndElement{Name{"", "hello"}},
CharData("\n "), CharData("\n "),
StartElement{Name{"", "query"}, []Attr{}},
CharData("What is it?"),
EndElement{Name{"", "query"}},
CharData("\n "),
StartElement{Name{"", "goodbye"}, []Attr{}}, StartElement{Name{"", "goodbye"}, []Attr{}},
EndElement{Name{"", "goodbye"}}, EndElement{Name{"", "goodbye"}},
CharData("\n "), CharData("\n "),
@ -74,6 +81,10 @@ var cookedTokens = []Token{
CharData("World <>'\" 白鵬翔"), CharData("World <>'\" 白鵬翔"),
EndElement{Name{"ns2", "hello"}}, EndElement{Name{"ns2", "hello"}},
CharData("\n "), CharData("\n "),
StartElement{Name{"ns2", "query"}, []Attr{}},
CharData("What is it?"),
EndElement{Name{"ns2", "query"}},
CharData("\n "),
StartElement{Name{"ns2", "goodbye"}, []Attr{}}, StartElement{Name{"ns2", "goodbye"}, []Attr{}},
EndElement{Name{"ns2", "goodbye"}}, EndElement{Name{"ns2", "goodbye"}},
CharData("\n "), CharData("\n "),
@ -156,6 +167,7 @@ var xmlInput = []string{
func TestRawToken(t *testing.T) { func TestRawToken(t *testing.T) {
d := NewDecoder(strings.NewReader(testInput)) d := NewDecoder(strings.NewReader(testInput))
d.Entity = testEntity
testRawToken(t, d, rawTokens) testRawToken(t, d, rawTokens)
} }
@ -164,8 +176,14 @@ const nonStrictInput = `
<tag>&unknown;entity</tag> <tag>&unknown;entity</tag>
<tag>&#123</tag> <tag>&#123</tag>
<tag>&#zzz;</tag> <tag>&#zzz;</tag>
<tag>&なまえ3;</tag>
<tag>&lt-gt;</tag>
<tag>&;</tag>
<tag>&0a;</tag>
` `
var nonStringEntity = map[string]string{"": "oops!", "0a": "oops!"}
var nonStrictTokens = []Token{ var nonStrictTokens = []Token{
CharData("\n"), CharData("\n"),
StartElement{Name{"", "tag"}, []Attr{}}, StartElement{Name{"", "tag"}, []Attr{}},
@ -184,6 +202,22 @@ var nonStrictTokens = []Token{
CharData("&#zzz;"), CharData("&#zzz;"),
EndElement{Name{"", "tag"}}, EndElement{Name{"", "tag"}},
CharData("\n"), CharData("\n"),
StartElement{Name{"", "tag"}, []Attr{}},
CharData("&なまえ3;"),
EndElement{Name{"", "tag"}},
CharData("\n"),
StartElement{Name{"", "tag"}, []Attr{}},
CharData("&lt-gt;"),
EndElement{Name{"", "tag"}},
CharData("\n"),
StartElement{Name{"", "tag"}, []Attr{}},
CharData("&;"),
EndElement{Name{"", "tag"}},
CharData("\n"),
StartElement{Name{"", "tag"}, []Attr{}},
CharData("&0a;"),
EndElement{Name{"", "tag"}},
CharData("\n"),
} }
func TestNonStrictRawToken(t *testing.T) { func TestNonStrictRawToken(t *testing.T) {
@ -317,6 +351,7 @@ func TestNestedDirectives(t *testing.T) {
func TestToken(t *testing.T) { func TestToken(t *testing.T) {
d := NewDecoder(strings.NewReader(testInput)) d := NewDecoder(strings.NewReader(testInput))
d.Entity = testEntity
for i, want := range cookedTokens { for i, want := range cookedTokens {
have, err := d.Token() have, err := d.Token()

View File

@ -176,8 +176,7 @@ func processPackage(fset *token.FileSet, files map[string]*ast.File) {
report(err) report(err)
return return
} }
_, err = types.Check(fset, pkg) if err = types.Check(fset, pkg, nil, nil); err != nil {
if err != nil {
report(err) report(err)
} }
} }

View File

@ -35,10 +35,12 @@ var tests = []struct {
// directories // directories
{filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"}, {filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/build"), "build"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"}, {filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "scanner"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"}, {filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/printer"), "printer"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
{filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "token"},
{filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"}, {filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"},
} }

View File

@ -9,7 +9,7 @@ import (
) )
// parseDoctype parses the data from a DoctypeToken into a name, // parseDoctype parses the data from a DoctypeToken into a name,
// public identifier, and system identifier. It returns a Node whose Type // public identifier, and system identifier. It returns a Node whose Type
// is DoctypeNode, whose Data is the name, and which has attributes // is DoctypeNode, whose Data is the name, and which has attributes
// named "system" and "public" for the two identifiers if they were present. // named "system" and "public" for the two identifiers if they were present.
// quirks is whether the document should be parsed in "quirks mode". // quirks is whether the document should be parsed in "quirks mode".

View File

@ -10,7 +10,7 @@ import (
"unicode/utf8" "unicode/utf8"
) )
// These replacements permit compatibility with old numeric entities that // These replacements permit compatibility with old numeric entities that
// assumed Windows-1252 encoding. // assumed Windows-1252 encoding.
// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference
var replacementTable = [...]rune{ var replacementTable = [...]rune{
@ -46,7 +46,7 @@ var replacementTable = [...]rune{
'\u009D', '\u009D',
'\u017E', '\u017E',
'\u0178', // Last entry is 0x9F. '\u0178', // Last entry is 0x9F.
// 0x00->'\uFFFD' is handled programmatically. // 0x00->'\uFFFD' is handled programmatically.
// 0x0D->'\u000D' is a no-op. // 0x0D->'\u000D' is a no-op.
} }

View File

@ -2027,7 +2027,7 @@ func Parse(r io.Reader) (*Node, error) {
return p.doc, nil return p.doc, nil
} }
// ParseFragment parses a fragment of HTML and returns the nodes that were // ParseFragment parses a fragment of HTML and returns the nodes that were
// found. If the fragment is the InnerHTML for an existing element, pass that // found. If the fragment is the InnerHTML for an existing element, pass that
// element in context. // element in context.
func ParseFragment(r io.Reader, context *Node) ([]*Node, error) { func ParseFragment(r io.Reader, context *Node) ([]*Node, error) {

View File

@ -35,7 +35,7 @@ type writer interface {
// children; the <a> is reparented to the <table>'s parent. However, calling // children; the <a> is reparented to the <table>'s parent. However, calling
// Parse on "<a><table><a>" does not return an error, but the result has an <a> // Parse on "<a><table><a>" does not return an error, but the result has an <a>
// element with an <a> child, and is therefore not 'well-formed'. // element with an <a> child, and is therefore not 'well-formed'.
// //
// Programmatically constructed trees are typically also 'well-formed', but it // Programmatically constructed trees are typically also 'well-formed', but it
// is possible to construct a tree that looks innocuous but, when rendered and // is possible to construct a tree that looks innocuous but, when rendered and
// re-parsed, results in a different tree. A simple example is that a solitary // re-parsed, results in a different tree. A simple example is that a solitary
@ -53,7 +53,7 @@ func Render(w io.Writer, n *Node) error {
return buf.Flush() return buf.Flush()
} }
// plaintextAbort is returned from render1 when a <plaintext> element // plaintextAbort is returned from render1 when a <plaintext> element
// has been rendered. No more end tags should be rendered after that. // has been rendered. No more end tags should be rendered after that.
var plaintextAbort = errors.New("html: internal error (plaintext abort)") var plaintextAbort = errors.New("html: internal error (plaintext abort)")

View File

@ -320,7 +320,7 @@ loop:
} }
// readRawEndTag attempts to read a tag like "</foo>", where "foo" is z.rawTag. // readRawEndTag attempts to read a tag like "</foo>", where "foo" is z.rawTag.
// If it succeeds, it backs up the input position to reconsume the tag and // If it succeeds, it backs up the input position to reconsume the tag and
// returns true. Otherwise it returns false. The opening "</" has already been // returns true. Otherwise it returns false. The opening "</" has already been
// consumed. // consumed.
func (z *Tokenizer) readRawEndTag() bool { func (z *Tokenizer) readRawEndTag() bool {

View File

@ -25,7 +25,7 @@ import (
// compacted. // compacted.
// - Compress secondary weights into 8 bits. // - Compress secondary weights into 8 bits.
// - Some LDML specs specify a context element. Currently we simply concatenate // - Some LDML specs specify a context element. Currently we simply concatenate
// those. Context can be implemented using the contraction trie. If Builder // those. Context can be implemented using the contraction trie. If Builder
// could analyze and detect when using a context makes sense, there is no // could analyze and detect when using a context makes sense, there is no
// need to expose this construct in the API. // need to expose this construct in the API.
@ -58,7 +58,9 @@ type Tailoring struct {
id string id string
builder *Builder builder *Builder
index *ordering index *ordering
// TODO: implement.
anchor *entry
before bool
} }
// NewBuilder returns a new Builder. // NewBuilder returns a new Builder.
@ -72,7 +74,7 @@ func NewBuilder() *Builder {
} }
} }
// Tailoring returns a Tailoring for the given locale. One should // Tailoring returns a Tailoring for the given locale. One should
// have completed all calls to Add before calling Tailoring. // have completed all calls to Add before calling Tailoring.
func (b *Builder) Tailoring(locale string) *Tailoring { func (b *Builder) Tailoring(locale string) *Tailoring {
t := &Tailoring{ t := &Tailoring{
@ -80,11 +82,12 @@ func (b *Builder) Tailoring(locale string) *Tailoring {
builder: b, builder: b,
index: b.root.clone(), index: b.root.clone(),
} }
t.index.id = t.id
b.locale = append(b.locale, t) b.locale = append(b.locale, t)
return t return t
} }
// Add adds an entry to the collation element table, mapping // Add adds an entry to the collation element table, mapping
// a slice of runes to a sequence of collation elements. // a slice of runes to a sequence of collation elements.
// A collation element is specified as list of weights: []int{primary, secondary, ...}. // A collation element is specified as list of weights: []int{primary, secondary, ...}.
// The entries are typically obtained from a collation element table // The entries are typically obtained from a collation element table
@ -95,7 +98,6 @@ func (b *Builder) Tailoring(locale string) *Tailoring {
// a value for each colelem that is a variable. (See the reference above.) // a value for each colelem that is a variable. (See the reference above.)
func (b *Builder) Add(runes []rune, colelems [][]int, variables []int) error { func (b *Builder) Add(runes []rune, colelems [][]int, variables []int) error {
str := string(runes) str := string(runes)
elems := make([][]int, len(colelems)) elems := make([][]int, len(colelems))
for i, ce := range colelems { for i, ce := range colelems {
elems[i] = append(elems[i], ce...) elems[i] = append(elems[i], ce...)
@ -127,7 +129,7 @@ func (b *Builder) Add(runes []rune, colelems [][]int, variables []int) error {
if ce[0] > b.varTop { if ce[0] > b.varTop {
b.varTop = ce[0] b.varTop = ce[0]
} }
} else if ce[0] > 0 { } else if ce[0] > 1 { // 1 is a special primary value reserved for FFFE
if ce[0] <= b.varTop { if ce[0] <= b.varTop {
return fmt.Errorf("primary value %X of non-variable is smaller than the highest variable %X", ce[0], b.varTop) return fmt.Errorf("primary value %X of non-variable is smaller than the highest variable %X", ce[0], b.varTop)
} }
@ -144,6 +146,21 @@ func (b *Builder) Add(runes []rune, colelems [][]int, variables []int) error {
return nil return nil
} }
func (t *Tailoring) setAnchor(anchor string) error {
anchor = norm.NFD.String(anchor)
a := t.index.find(anchor)
if a == nil {
a = t.index.newEntry(anchor, nil)
a.implicit = true
for _, r := range []rune(anchor) {
e := t.index.find(string(r))
e.lock = true
}
}
t.anchor = a
return nil
}
// SetAnchor sets the point after which elements passed in subsequent calls to // SetAnchor sets the point after which elements passed in subsequent calls to
// Insert will be inserted. It is equivalent to the reset directive in an LDML // Insert will be inserted. It is equivalent to the reset directive in an LDML
// specification. See Insert for an example. // specification. See Insert for an example.
@ -151,14 +168,20 @@ func (b *Builder) Add(runes []rune, colelems [][]int, variables []int) error {
// <first_tertiary_ignorable/>, <last_teriary_ignorable/>, <first_primary_ignorable/>, // <first_tertiary_ignorable/>, <last_teriary_ignorable/>, <first_primary_ignorable/>,
// and <last_non_ignorable/>. // and <last_non_ignorable/>.
func (t *Tailoring) SetAnchor(anchor string) error { func (t *Tailoring) SetAnchor(anchor string) error {
// TODO: implement. if err := t.setAnchor(anchor); err != nil {
return err
}
t.before = false
return nil return nil
} }
// SetAnchorBefore is similar to SetAnchor, except that subsequent calls to // SetAnchorBefore is similar to SetAnchor, except that subsequent calls to
// Insert will insert entries before the anchor. // Insert will insert entries before the anchor.
func (t *Tailoring) SetAnchorBefore(anchor string) error { func (t *Tailoring) SetAnchorBefore(anchor string) error {
// TODO: implement. if err := t.setAnchor(anchor); err != nil {
return err
}
t.before = true
return nil return nil
} }
@ -171,7 +194,7 @@ func (t *Tailoring) SetAnchorBefore(anchor string) error {
// See http://www.unicode.org/reports/tr10/#Tailoring_Example for details // See http://www.unicode.org/reports/tr10/#Tailoring_Example for details
// on parametric tailoring and http://unicode.org/reports/tr35/#Collation_Elements // on parametric tailoring and http://unicode.org/reports/tr35/#Collation_Elements
// for full details on LDML. // for full details on LDML.
// //
// Examples: create a tailoring for Swedish, where "ä" is ordered after "z" // Examples: create a tailoring for Swedish, where "ä" is ordered after "z"
// at the primary sorting level: // at the primary sorting level:
// t := b.Tailoring("se") // t := b.Tailoring("se")
@ -195,7 +218,112 @@ func (t *Tailoring) SetAnchorBefore(anchor string) error {
// t.SetAnchor("<last_primary_ignorable/>") // t.SetAnchor("<last_primary_ignorable/>")
// t.Insert(collate.Primary, "0", "") // t.Insert(collate.Primary, "0", "")
func (t *Tailoring) Insert(level collate.Level, str, extend string) error { func (t *Tailoring) Insert(level collate.Level, str, extend string) error {
// TODO: implement. if t.anchor == nil {
return fmt.Errorf("%s:Insert: no anchor point set for tailoring of %s", t.id, str)
}
str = norm.NFD.String(str)
e := t.index.find(str)
if e == nil {
e = t.index.newEntry(str, nil)
} else if e.logical != noAnchor {
return fmt.Errorf("%s:Insert: cannot reinsert logical reset position %q", t.id, e.str)
}
if e.lock {
return fmt.Errorf("%s:Insert: cannot reinsert element %q", t.id, e.str)
}
a := t.anchor
// Find the first element after the anchor which differs at a level smaller or
// equal to the given level. Then insert at this position.
// See http://unicode.org/reports/tr35/#Collation_Elements, Section 5.14.5 for details.
e.before = t.before
if t.before {
t.before = false
if a.prev == nil {
a.insertBefore(e)
} else {
for a = a.prev; a.level > level; a = a.prev {
}
a.insertAfter(e)
}
e.level = level
} else {
for ; a.level > level; a = a.next {
}
e.level = a.level
if a != e {
a.insertAfter(e)
a.level = level
} else {
// We don't set a to prev itself. This has the effect of the entry
// getting new collation elements that are an increment of itself.
// This is intentional.
a.prev.level = level
}
}
e.extend = norm.NFD.String(extend)
e.exclude = false
e.elems = nil
t.anchor = e
return nil
}
func (o *ordering) getWeight(e *entry) [][]int {
if len(e.elems) == 0 && e.logical == noAnchor {
if e.implicit {
for _, r := range e.runes {
e.elems = append(e.elems, o.getWeight(o.find(string(r)))...)
}
} else if e.before {
count := [collate.Identity + 1]int{}
a := e
for ; a.elems == nil && !a.implicit; a = a.next {
count[a.level]++
}
e.elems = append([][]int(nil), make([]int, len(a.elems[0])))
copy(e.elems[0], a.elems[0])
for i := collate.Primary; i < collate.Quaternary; i++ {
if count[i] != 0 {
e.elems[0][i] -= count[i]
break
}
}
if e.prev != nil {
o.verifyWeights(e.prev, e, e.prev.level)
}
} else {
prev := e.prev
e.elems = nextWeight(prev.level, o.getWeight(prev))
o.verifyWeights(e, e.next, e.level)
}
}
return e.elems
}
func (o *ordering) addExtension(e *entry) {
if ex := o.find(e.extend); ex != nil {
e.elems = append(e.elems, ex.elems...)
} else {
for _, r := range []rune(e.extend) {
e.elems = append(e.elems, o.find(string(r)).elems...)
}
}
e.extend = ""
}
func (o *ordering) verifyWeights(a, b *entry, level collate.Level) error {
if level == collate.Identity || b == nil || b.elems == nil || a.elems == nil {
return nil
}
for i := collate.Primary; i < level; i++ {
if a.elems[0][i] < b.elems[0][i] {
return nil
}
}
if a.elems[0][level] >= b.elems[0][level] {
err := fmt.Errorf("%s:overflow: collation elements of %q (%X) overflows those of %q (%X) at level %d (%X >= %X)", o.id, a.str, a.runes, b.str, b.runes, level, a.elems, b.elems)
log.Println(err)
// TODO: return the error instead, or better, fix the conflicting entry by making room.
}
return nil return nil
} }
@ -205,7 +333,19 @@ func (b *Builder) error(e error) {
} }
} }
func (b *Builder) errorID(locale string, e error) {
if e != nil {
b.err = fmt.Errorf("%s:%v", locale, e)
}
}
func (b *Builder) buildOrdering(o *ordering) { func (b *Builder) buildOrdering(o *ordering) {
for _, e := range o.ordered {
o.getWeight(e)
}
for _, e := range o.ordered {
o.addExtension(e)
}
o.sort() o.sort()
simplify(o) simplify(o)
b.processExpansions(o) // requires simplify b.processExpansions(o) // requires simplify
@ -215,7 +355,7 @@ func (b *Builder) buildOrdering(o *ordering) {
for e := o.front(); e != nil; e, _ = e.nextIndexed() { for e := o.front(); e != nil; e, _ = e.nextIndexed() {
if !e.skip() { if !e.skip() {
ce, err := e.encode() ce, err := e.encode()
b.error(err) b.errorID(o.id, err)
t.insert(e.runes[0], ce) t.insert(e.runes[0], ce)
} }
} }
@ -252,7 +392,11 @@ func (b *Builder) Build() (*collate.Collator, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return collate.Init(t), nil c := collate.Init(t)
if c == nil {
panic("generated table of incompatible type")
}
return c, nil
} }
// Build builds a Collator for Tailoring t. // Build builds a Collator for Tailoring t.
@ -308,6 +452,10 @@ func reproducibleFromNFKD(e *entry, exp, nfkd [][]int) bool {
if i >= 2 && ce[2] != maxTertiary { if i >= 2 && ce[2] != maxTertiary {
return false return false
} }
if _, err := makeCE(ce); err != nil {
// Simply return false. The error will be caught elsewhere.
return false
}
} }
return true return true
} }
@ -332,12 +480,11 @@ func simplify(o *ordering) {
e.remove() e.remove()
} }
} }
// Tag entries for which the runes NFKD decompose to identical values. // Tag entries for which the runes NFKD decompose to identical values.
for e := o.front(); e != nil; e, _ = e.nextIndexed() { for e := o.front(); e != nil; e, _ = e.nextIndexed() {
s := e.str s := e.str
nfkd := norm.NFKD.String(s) nfkd := norm.NFKD.String(s)
if len(e.runes) > 1 || keep[e.runes[0]] || nfkd == s { if e.decompose || len(e.runes) > 1 || len(e.elems) == 1 || keep[e.runes[0]] || nfkd == s {
continue continue
} }
if reproducibleFromNFKD(e, e.elems, o.genColElems(nfkd)) { if reproducibleFromNFKD(e, e.elems, o.genColElems(nfkd)) {
@ -459,7 +606,7 @@ func (b *Builder) processContractions(o *ordering) {
elems := []uint32{} elems := []uint32{}
for _, e := range es { for _, e := range es {
ce, err := e.encodeBase() ce, err := e.encodeBase()
b.error(err) b.errorID(o.id, err)
elems = append(elems, ce) elems = append(elems, ce)
} }
key = fmt.Sprintf("%v", elems) key = fmt.Sprintf("%v", elems)

View File

@ -26,7 +26,7 @@ const (
// For normal collation elements, we assume that a collation element either has // For normal collation elements, we assume that a collation element either has
// a primary or non-default secondary value, not both. // a primary or non-default secondary value, not both.
// Collation elements with a primary value are of the form // Collation elements with a primary value are of the form
// 010ppppp pppppppp pppppppp ssssssss // 01pppppp pppppppp ppppppp0 ssssssss
// - p* is primary collation value // - p* is primary collation value
// - s* is the secondary collation value // - s* is the secondary collation value
// or // or
@ -67,10 +67,10 @@ func makeCE(weights []int) (uint32, error) {
if weights[1] >= 1<<maxSecondaryCompactBits { if weights[1] >= 1<<maxSecondaryCompactBits {
return 0, fmt.Errorf("makeCE: secondary weight with non-zero primary out of bounds: %x >= %x", weights[1], 1<<maxSecondaryCompactBits) return 0, fmt.Errorf("makeCE: secondary weight with non-zero primary out of bounds: %x >= %x", weights[1], 1<<maxSecondaryCompactBits)
} }
ce = uint32(weights[0]<<maxSecondaryCompactBits + weights[1]) ce = uint32(weights[0]<<(maxSecondaryCompactBits+1) + weights[1])
ce |= isPrimary ce |= isPrimary
} else { } else {
d := weights[1] - defaultSecondary d := weights[1] - defaultSecondary + 4
if d >= 1<<maxSecondaryDiffBits || d < 0 { if d >= 1<<maxSecondaryDiffBits || d < 0 {
return 0, fmt.Errorf("makeCE: secondary weight diff out of bounds: %x < 0 || %x > %x", d, d, 1<<maxSecondaryDiffBits) return 0, fmt.Errorf("makeCE: secondary weight diff out of bounds: %x < 0 || %x > %x", d, d, 1<<maxSecondaryDiffBits)
} }
@ -132,7 +132,7 @@ func makeExpandIndex(index int) (uint32, error) {
return expandID + uint32(index), nil return expandID + uint32(index), nil
} }
// Each list of collation elements corresponding to an expansion starts with // Each list of collation elements corresponding to an expansion starts with
// a header indicating the length of the sequence. // a header indicating the length of the sequence.
func makeExpansionHeader(n int) (uint32, error) { func makeExpansionHeader(n int) (uint32, error) {
return uint32(n), nil return uint32(n), nil
@ -199,7 +199,7 @@ func implicitPrimary(r rune) int {
return int(r) + otherOffset return int(r) + otherOffset
} }
// convertLargeWeights converts collation elements with large // convertLargeWeights converts collation elements with large
// primaries (either double primaries or for illegal runes) // primaries (either double primaries or for illegal runes)
// to our own representation. // to our own representation.
// A CJK character C is represented in the DUCET as // A CJK character C is represented in the DUCET as
@ -258,21 +258,31 @@ func convertLargeWeights(elems [][]int) (res [][]int, err error) {
// nextWeight computes the first possible collation weights following elems // nextWeight computes the first possible collation weights following elems
// for the given level. // for the given level.
func nextWeight(level collate.Level, elems [][]int) [][]int { func nextWeight(level collate.Level, elems [][]int) [][]int {
nce := make([][]int, len(elems)) if level == collate.Identity {
copy(nce, elems) next := make([][]int, len(elems))
copy(next, elems)
if level != collate.Identity { return next
nce[0] = make([]int, len(elems[0])) }
copy(nce[0], elems[0]) next := [][]int{make([]int, len(elems[0]))}
nce[0][level]++ copy(next[0], elems[0])
if level < collate.Secondary { next[0][level]++
nce[0][collate.Secondary] = defaultSecondary if level < collate.Secondary {
next[0][collate.Secondary] = defaultSecondary
}
if level < collate.Tertiary {
next[0][collate.Tertiary] = defaultTertiary
}
// Filter entries that cannot influence ordering.
for _, ce := range elems[1:] {
skip := true
for i := collate.Primary; i < level; i++ {
skip = skip && ce[i] == 0
} }
if level < collate.Tertiary { if !skip {
nce[0][collate.Tertiary] = defaultTertiary next = append(next, ce)
} }
} }
return nce return next
} }
func nextVal(elems [][]int, i int, level collate.Level) (index, value int) { func nextVal(elems [][]int, i int, level collate.Level) (index, value int) {

View File

@ -34,10 +34,10 @@ func decompCE(in []int) (ce uint32, err error) {
var ceTests = []ceTest{ var ceTests = []ceTest{
{normalCE, []int{0, 0, 0}, 0x80000000}, {normalCE, []int{0, 0, 0}, 0x80000000},
{normalCE, []int{0, 0x28, 3}, 0x80002803}, {normalCE, []int{0, 0x28, 3}, 0x80002803},
{normalCE, []int{100, defaultSecondary, 3}, 0x0000C803}, {normalCE, []int{100, defaultSecondary, 3}, 0x0000C883},
// non-ignorable primary with non-default secondary // non-ignorable primary with non-default secondary
{normalCE, []int{100, 0x28, defaultTertiary}, 0x40006428}, {normalCE, []int{100, 0x28, defaultTertiary}, 0x4000C828},
{normalCE, []int{100, defaultSecondary + 8, 3}, 0x0000C903}, {normalCE, []int{100, defaultSecondary + 8, 3}, 0x0000C983},
{normalCE, []int{100, 0, 3}, 0xFFFF}, // non-ignorable primary with non-supported secondary {normalCE, []int{100, 0, 3}, 0xFFFF}, // non-ignorable primary with non-supported secondary
{normalCE, []int{100, 1, 3}, 0xFFFF}, {normalCE, []int{100, 1, 3}, 0xFFFF},
{normalCE, []int{1 << maxPrimaryBits, defaultSecondary, 0}, 0xFFFF}, {normalCE, []int{1 << maxPrimaryBits, defaultSecondary, 0}, 0xFFFF},
@ -114,18 +114,24 @@ var nextWeightTests = []weightsTest{
}, },
} }
var extra = []int{200, 32, 8, 0} var extra = [][]int{{200, 32, 8, 0}, {0, 32, 8, 0}, {0, 0, 8, 0}, {0, 0, 0, 0}}
func TestNextWeight(t *testing.T) { func TestNextWeight(t *testing.T) {
for i, tt := range nextWeightTests { for i, tt := range nextWeightTests {
test := func(tt weightsTest, a, gold [][]int) { test := func(l collate.Level, tt weightsTest, a, gold [][]int) {
res := nextWeight(tt.level, a) res := nextWeight(tt.level, a)
if !equalCEArrays(gold, res) { if !equalCEArrays(gold, res) {
t.Errorf("%d: expected weights %d; found %d", i, tt.b, res) t.Errorf("%d:%d: expected weights %d; found %d", i, l, gold, res)
}
}
test(-1, tt, tt.a, tt.b)
for l := collate.Primary; l <= collate.Tertiary; l++ {
if tt.level <= l {
test(l, tt, append(tt.a, extra[l]), tt.b)
} else {
test(l, tt, append(tt.a, extra[l]), append(tt.b, extra[l]))
} }
} }
test(tt, tt.a, tt.b)
test(tt, append(tt.a, extra), append(tt.b, extra))
} }
} }
@ -137,7 +143,7 @@ var compareTests = []weightsTest{
0, 0,
}, },
{ {
[][]int{{100, 20, 5, 0}, extra}, [][]int{{100, 20, 5, 0}, extra[0]},
[][]int{{100, 20, 5, 1}}, [][]int{{100, 20, 5, 1}},
collate.Primary, collate.Primary,
1, 1,
@ -192,6 +198,6 @@ func TestCompareWeights(t *testing.T) {
} }
} }
test(tt, tt.a, tt.b) test(tt, tt.a, tt.b)
test(tt, append(tt.a, extra), append(tt.b, extra)) test(tt, append(tt.a, extra[0]), append(tt.b, extra[0]))
} }
} }

View File

@ -15,7 +15,7 @@ import (
// This file contains code for detecting contractions and generating // This file contains code for detecting contractions and generating
// the necessary tables. // the necessary tables.
// Any Unicode Collation Algorithm (UCA) table entry that has more than // Any Unicode Collation Algorithm (UCA) table entry that has more than
// one rune one the left-hand side is called a contraction. // one rune one the left-hand side is called a contraction.
// See http://www.unicode.org/reports/tr10/#Contractions for more details. // See http://www.unicode.org/reports/tr10/#Contractions for more details.
// //
// We define the following terms: // We define the following terms:
@ -27,7 +27,7 @@ import (
// A rune may be both a initial and a non-initial and may be so in // A rune may be both a initial and a non-initial and may be so in
// many contractions. An initial may typically also appear by itself. // many contractions. An initial may typically also appear by itself.
// In case of ambiguities, the UCA requires we match the longest // In case of ambiguities, the UCA requires we match the longest
// contraction. // contraction.
// //
// Many contraction rules share the same set of possible suffixes. // Many contraction rules share the same set of possible suffixes.
// We store sets of suffixes in a trie that associates an index with // We store sets of suffixes in a trie that associates an index with
@ -39,14 +39,14 @@ import (
// is represented as a subsequence of ctEntries, where each entry corresponds to // is represented as a subsequence of ctEntries, where each entry corresponds to
// a possible match of a next character in the search string. An entry // a possible match of a next character in the search string. An entry
// also includes the length and offset to the next sequence of entries // also includes the length and offset to the next sequence of entries
// to check in case of a match. // to check in case of a match.
const ( const (
final = 0 final = 0
noIndex = 0xFF noIndex = 0xFF
) )
// ctEntry associates to a matching byte an offset and/or next sequence of // ctEntry associates to a matching byte an offset and/or next sequence of
// bytes to check. A ctEntry c is called final if a match means that the // bytes to check. A ctEntry c is called final if a match means that the
// longest suffix has been found. An entry c is final if c.n == 0. // longest suffix has been found. An entry c is final if c.n == 0.
// A single final entry can match a range of characters to an offset. // A single final entry can match a range of characters to an offset.
@ -58,7 +58,7 @@ const (
// {'a', 1, 1, noIndex}, // 'a' by itself does not match, so i is 0xFF. // {'a', 1, 1, noIndex}, // 'a' by itself does not match, so i is 0xFF.
// {'b', 'c', 0, 1}, // "ab" -> 1, "ac" -> 2 // {'b', 'c', 0, 1}, // "ab" -> 1, "ac" -> 2
// } // }
// //
// The suffix strings "ab", "abc", "abd", and "abcd" can be represented as: // The suffix strings "ab", "abc", "abd", and "abcd" can be represented as:
// []ctEntry{ // []ctEntry{
// {'a', 1, 1, noIndex}, // 'a' must be followed by 'b'. // {'a', 1, 1, noIndex}, // 'a' must be followed by 'b'.
@ -72,7 +72,7 @@ type ctEntry struct {
l uint8 // non-final: byte value to match; final: lowest match in range. l uint8 // non-final: byte value to match; final: lowest match in range.
h uint8 // non-final: relative index to next block; final: highest match in range. h uint8 // non-final: relative index to next block; final: highest match in range.
n uint8 // non-final: length of next block; final: final n uint8 // non-final: length of next block; final: final
i uint8 // result offset. Will be noIndex if more bytes are needed to complete. i uint8 // result offset. Will be noIndex if more bytes are needed to complete.
} }
// contractTrieSet holds a set of contraction tries. The tries are stored // contractTrieSet holds a set of contraction tries. The tries are stored

View File

@ -26,16 +26,21 @@ const (
// Collation Element Table. // Collation Element Table.
// See http://www.unicode.org/Public/UCA/6.0.0/allkeys.txt. // See http://www.unicode.org/Public/UCA/6.0.0/allkeys.txt.
type entry struct { type entry struct {
runes []rune str string // same as string(runes)
elems [][]int // the collation elements for runes runes []rune
str string // same as string(runes) elems [][]int // the collation elements
extend string // weights of extend to be appended to elems
before bool // weights relative to next instead of previous.
lock bool // entry is used in extension and can no longer be moved.
// prev, next, and level are used to keep track of tailorings. // prev, next, and level are used to keep track of tailorings.
prev, next *entry prev, next *entry
level collate.Level // next differs at this level level collate.Level // next differs at this level
skipRemove bool // do not unlink when removed
decompose bool // can use NFKD decomposition to generate elems decompose bool // can use NFKD decomposition to generate elems
exclude bool // do not include in table exclude bool // do not include in table
implicit bool // derived, is not included in the list
logical logicalAnchor logical logicalAnchor
expansionIndex int // used to store index into expansion table expansionIndex int // used to store index into expansion table
@ -44,8 +49,8 @@ type entry struct {
} }
func (e *entry) String() string { func (e *entry) String() string {
return fmt.Sprintf("%X -> %X (ch:%x; ci:%d, ei:%d)", return fmt.Sprintf("%X (%q) -> %X (ch:%x; ci:%d, ei:%d)",
e.runes, e.elems, e.contractionHandle, e.contractionIndex, e.expansionIndex) e.runes, e.str, e.elems, e.contractionHandle, e.contractionIndex, e.expansionIndex)
} }
func (e *entry) skip() bool { func (e *entry) skip() bool {
@ -71,7 +76,7 @@ func (e *entry) contractionStarter() bool {
// examples of entries that will not be indexed. // examples of entries that will not be indexed.
func (e *entry) nextIndexed() (*entry, collate.Level) { func (e *entry) nextIndexed() (*entry, collate.Level) {
level := e.level level := e.level
for e = e.next; e != nil && e.exclude; e = e.next { for e = e.next; e != nil && (e.exclude || len(e.elems) == 0); e = e.next {
if e.level < level { if e.level < level {
level = e.level level = e.level
} }
@ -87,16 +92,20 @@ func (e *entry) remove() {
if e.logical != noAnchor { if e.logical != noAnchor {
log.Fatalf("may not remove anchor %q", e.str) log.Fatalf("may not remove anchor %q", e.str)
} }
if e.prev != nil { // TODO: need to set e.prev.level to e.level if e.level is smaller?
e.prev.next = e.next
}
if e.next != nil {
e.next.prev = e.prev
}
e.elems = nil e.elems = nil
if !e.skipRemove {
if e.prev != nil {
e.prev.next = e.next
}
if e.next != nil {
e.next.prev = e.prev
}
}
e.skipRemove = false
} }
// insertAfter inserts t after e. // insertAfter inserts n after e.
func (e *entry) insertAfter(n *entry) { func (e *entry) insertAfter(n *entry) {
if e == n { if e == n {
panic("e == anchor") panic("e == anchor")
@ -109,10 +118,31 @@ func (e *entry) insertAfter(n *entry) {
n.next = e.next n.next = e.next
n.prev = e n.prev = e
e.next.prev = n if e.next != nil {
e.next.prev = n
}
e.next = n e.next = n
} }
// insertBefore inserts n before e.
func (e *entry) insertBefore(n *entry) {
if e == n {
panic("e == anchor")
}
if e == nil {
panic("unexpected nil anchor")
}
n.remove()
n.decompose = false // redo decomposition test
n.prev = e.prev
n.next = e
if e.prev != nil {
e.prev.next = n
}
e.prev = n
}
func (e *entry) encodeBase() (ce uint32, err error) { func (e *entry) encodeBase() (ce uint32, err error) {
switch { switch {
case e.expansion(): case e.expansion():
@ -178,6 +208,7 @@ func (s sortedEntries) Less(i, j int) bool {
} }
type ordering struct { type ordering struct {
id string
entryMap map[string]*entry entryMap map[string]*entry
ordered []*entry ordered []*entry
handle *trieHandle handle *trieHandle
@ -187,7 +218,14 @@ type ordering struct {
// Note that insert simply appends e to ordered. To reattain a sorted // Note that insert simply appends e to ordered. To reattain a sorted
// order, o.sort() should be called. // order, o.sort() should be called.
func (o *ordering) insert(e *entry) { func (o *ordering) insert(e *entry) {
o.entryMap[e.str] = e if e.logical == noAnchor {
o.entryMap[e.str] = e
} else {
// Use key format as used in UCA rules.
o.entryMap[fmt.Sprintf("[%s]", e.str)] = e
// Also add index entry for XML format.
o.entryMap[fmt.Sprintf("<%s/>", strings.Replace(e.str, " ", "_", -1))] = e
}
o.ordered = append(o.ordered, e) o.ordered = append(o.ordered, e)
} }
@ -236,13 +274,13 @@ func makeRootOrdering() ordering {
entryMap: make(map[string]*entry), entryMap: make(map[string]*entry),
} }
insert := func(typ logicalAnchor, s string, ce []int) { insert := func(typ logicalAnchor, s string, ce []int) {
// Use key format as used in UCA rules. e := &entry{
e := o.newEntry(fmt.Sprintf("[%s]", s), [][]int{ce}) elems: [][]int{ce},
// Also add index entry for XML format. str: s,
o.entryMap[fmt.Sprintf("<%s/>", strings.Replace(s, " ", "_", -1))] = e exclude: true,
e.runes = nil logical: typ,
e.exclude = true }
e.logical = typ o.insert(e)
} }
insert(firstAnchor, "first tertiary ignorable", []int{0, 0, 0, 0}) insert(firstAnchor, "first tertiary ignorable", []int{0, 0, 0, 0})
insert(lastAnchor, "last tertiary ignorable", []int{0, 0, 0, max}) insert(lastAnchor, "last tertiary ignorable", []int{0, 0, 0, max})
@ -252,6 +290,29 @@ func makeRootOrdering() ordering {
return o return o
} }
// patchForInsert eleminates entries from the list with more than one collation element.
// The next and prev fields of the eliminated entries still point to appropriate
// values in the newly created list.
// It requires that sort has been called.
func (o *ordering) patchForInsert() {
for i := 0; i < len(o.ordered)-1; {
e := o.ordered[i]
lev := e.level
n := e.next
for ; n != nil && len(n.elems) > 1; n = n.next {
if n.level < lev {
lev = n.level
}
n.skipRemove = true
}
for ; o.ordered[i] != n; i++ {
o.ordered[i].level = lev
o.ordered[i].next = n
o.ordered[i+1].prev = e
}
}
}
// clone copies all ordering of es into a new ordering value. // clone copies all ordering of es into a new ordering value.
func (o *ordering) clone() *ordering { func (o *ordering) clone() *ordering {
o.sort() o.sort()
@ -270,6 +331,7 @@ func (o *ordering) clone() *ordering {
oo.insert(ne) oo.insert(ne)
} }
oo.sort() // link all ordering. oo.sort() // link all ordering.
oo.patchForInsert()
return &oo return &oo
} }

View File

@ -128,6 +128,9 @@ func TestInsertAfter(t *testing.T) {
last.insertAfter(es[i]) last.insertAfter(es[i])
last = es[i] last = es[i]
} }
for _, e := range es {
e.elems = es[0].elems
}
e := es[0] e := es[0]
for _, i := range perm { for _, i := range perm {
e, _ = e.nextIndexed() e, _ = e.nextIndexed()
@ -139,6 +142,34 @@ func TestInsertAfter(t *testing.T) {
} }
} }
func TestInsertBefore(t *testing.T) {
const n = 5
orig := makeList(n)
perm := make([]int, n)
for i := range perm {
perm[i] = i + 1
}
for ok := true; ok; ok = nextPerm(perm) {
es := makeList(n)
last := es[len(es)-1]
for _, i := range perm {
last.insertBefore(es[i])
last = es[i]
}
for _, e := range es {
e.elems = es[0].elems
}
e := es[0]
for i := n - 1; i >= 0; i-- {
e, _ = e.nextIndexed()
if e.runes[0] != rune(perm[i]) {
t.Errorf("%d:%d: expected entry %X; found %X", perm, i, orig[i].runes, e.runes)
break
}
}
}
}
type entryLessTest struct { type entryLessTest struct {
a, b *entry a, b *entry
res bool res bool

View File

@ -4,7 +4,7 @@
// The trie in this file is used to associate the first full character // The trie in this file is used to associate the first full character
// in a UTF-8 string to a collation element. // in a UTF-8 string to a collation element.
// All but the last byte in a UTF-8 byte sequence are // All but the last byte in a UTF-8 byte sequence are
// used to look up offsets in the index table to be used for the next byte. // used to look up offsets in the index table to be used for the next byte.
// The last byte is used to index into a table of collation elements. // The last byte is used to index into a table of collation elements.
// This file contains the code for the generation of the trie. // This file contains the code for the generation of the trie.
@ -35,10 +35,11 @@ type trie struct {
// trieNode is the intermediate trie structure used for generating a trie. // trieNode is the intermediate trie structure used for generating a trie.
type trieNode struct { type trieNode struct {
index []*trieNode index []*trieNode
value []uint32 value []uint32
b byte b byte
ref uint16 refValue uint16
refIndex uint16
} }
func newNode() *trieNode { func newNode() *trieNode {
@ -108,18 +109,20 @@ func (b *trieBuilder) computeOffsets(n *trieNode) *trieNode {
hasher := fnv.New32() hasher := fnv.New32()
if n.index != nil { if n.index != nil {
for i, nn := range n.index { for i, nn := range n.index {
v := uint16(0) var vi, vv uint16
if nn != nil { if nn != nil {
nn = b.computeOffsets(nn) nn = b.computeOffsets(nn)
n.index[i] = nn n.index[i] = nn
v = nn.ref vi = nn.refIndex
vv = nn.refValue
} }
hasher.Write([]byte{byte(v >> 8), byte(v)}) hasher.Write([]byte{byte(vi >> 8), byte(vi)})
hasher.Write([]byte{byte(vv >> 8), byte(vv)})
} }
h := hasher.Sum32() h := hasher.Sum32()
nn, ok := b.lookupBlockIdx[h] nn, ok := b.lookupBlockIdx[h]
if !ok { if !ok {
n.ref = uint16(len(b.lookupBlocks)) - blockOffset n.refIndex = uint16(len(b.lookupBlocks)) - blockOffset
b.lookupBlocks = append(b.lookupBlocks, n) b.lookupBlocks = append(b.lookupBlocks, n)
b.lookupBlockIdx[h] = n b.lookupBlockIdx[h] = n
} else { } else {
@ -132,7 +135,8 @@ func (b *trieBuilder) computeOffsets(n *trieNode) *trieNode {
h := hasher.Sum32() h := hasher.Sum32()
nn, ok := b.valueBlockIdx[h] nn, ok := b.valueBlockIdx[h]
if !ok { if !ok {
n.ref = uint16(len(b.valueBlocks)) - blockOffset n.refValue = uint16(len(b.valueBlocks)) - blockOffset
n.refIndex = n.refValue
b.valueBlocks = append(b.valueBlocks, n) b.valueBlocks = append(b.valueBlocks, n)
b.valueBlockIdx[h] = n b.valueBlockIdx[h] = n
} else { } else {
@ -150,7 +154,8 @@ func (b *trieBuilder) addStartValueBlock(n *trieNode) uint16 {
h := hasher.Sum32() h := hasher.Sum32()
nn, ok := b.valueBlockIdx[h] nn, ok := b.valueBlockIdx[h]
if !ok { if !ok {
n.ref = uint16(len(b.valueBlocks)) n.refValue = uint16(len(b.valueBlocks))
n.refIndex = n.refValue
b.valueBlocks = append(b.valueBlocks, n) b.valueBlocks = append(b.valueBlocks, n)
// Add a dummy block to accommodate the double block size. // Add a dummy block to accommodate the double block size.
b.valueBlocks = append(b.valueBlocks, nil) b.valueBlocks = append(b.valueBlocks, nil)
@ -158,7 +163,7 @@ func (b *trieBuilder) addStartValueBlock(n *trieNode) uint16 {
} else { } else {
n = nn n = nn
} }
return n.ref return n.refValue
} }
func genValueBlock(t *trie, n *trieNode) { func genValueBlock(t *trie, n *trieNode) {
@ -173,7 +178,11 @@ func genLookupBlock(t *trie, n *trieNode) {
for _, nn := range n.index { for _, nn := range n.index {
v := uint16(0) v := uint16(0)
if nn != nil { if nn != nil {
v = nn.ref if n.index != nil {
v = nn.refIndex
} else {
v = nn.refValue
}
} }
t.index = append(t.index, v) t.index = append(t.index, v)
} }
@ -192,7 +201,7 @@ func (b *trieBuilder) addTrie(n *trieNode) *trieHandle {
} }
n = b.computeOffsets(n) n = b.computeOffsets(n)
// Offset by one extra block as the first byte starts at 0xC0 instead of 0x80. // Offset by one extra block as the first byte starts at 0xC0 instead of 0x80.
h.lookupStart = n.ref - 1 h.lookupStart = n.refIndex - 1
return h return h
} }

View File

@ -10,7 +10,7 @@ import (
"testing" "testing"
) )
// We take the smallest, largest and an arbitrary value for each // We take the smallest, largest and an arbitrary value for each
// of the UTF-8 sequence lengths. // of the UTF-8 sequence lengths.
var testRunes = []rune{ var testRunes = []rune{
0x01, 0x0C, 0x7F, // 1-byte sequences 0x01, 0x0C, 0x7F, // 1-byte sequences

View File

@ -8,16 +8,6 @@ import (
"unicode" "unicode"
) )
// weights holds the decoded weights per collation level.
type weights struct {
primary uint32
secondary uint16
tertiary uint8
// TODO: compute quaternary on the fly or compress this value into 8 bits
// such that weights fit within 64bit.
quaternary uint32
}
const ( const (
defaultSecondary = 0x20 defaultSecondary = 0x20
defaultTertiary = 0x2 defaultTertiary = 0x2
@ -69,7 +59,7 @@ func (ce colElem) ctype() ceType {
// For normal collation elements, we assume that a collation element either has // For normal collation elements, we assume that a collation element either has
// a primary or non-default secondary value, not both. // a primary or non-default secondary value, not both.
// Collation elements with a primary value are of the form // Collation elements with a primary value are of the form
// 010ppppp pppppppp pppppppp ssssssss // 01pppppp pppppppp ppppppp0 ssssssss
// - p* is primary collation value // - p* is primary collation value
// - s* is the secondary collation value // - s* is the secondary collation value
// or // or
@ -82,25 +72,87 @@ func (ce colElem) ctype() ceType {
// - 16 BMP implicit -> weight // - 16 BMP implicit -> weight
// - 8 bit s // - 8 bit s
// - default tertiary // - default tertiary
func splitCE(ce colElem) weights { // 11qqqqqq qqqqqqqq qqqqqqq0 00000000
const primaryMask = 0x40000000 // - q* quaternary value
const secondaryMask = 0x80000000 const (
w := weights{} ceTypeMask = 0xC0000000
if ce&primaryMask != 0 { ceType1 = 0x40000000
w.tertiary = defaultTertiary ceType2 = 0x00000000
w.secondary = uint16(uint8(ce)) ceType3 = 0x80000000
w.primary = uint32((ce >> 8) & 0x1FFFFF) ceTypeQ = 0xC0000000
} else if ce&secondaryMask == 0 { ceIgnore = ceType3
w.tertiary = uint8(ce & 0x1F) firstNonPrimary = 0x80000000
ce >>= 5 secondaryMask = 0x80000000
w.secondary = defaultSecondary + uint16(ce&0xF) hasTertiaryMask = 0x40000000
ce >>= 4 primaryValueMask = 0x3FFFFE00
w.primary = uint32(ce) primaryShift = 9
} else { compactSecondaryShift = 5
w.tertiary = uint8(ce) minCompactSecondary = defaultSecondary - 4
w.secondary = uint16(ce >> 8) )
func makeImplicitCE(primary int) colElem {
return ceType1 | colElem(primary<<primaryShift) | defaultSecondary
}
func makeQuaternary(primary int) colElem {
return ceTypeQ | colElem(primary<<primaryShift)
}
func (ce colElem) primary() int {
if ce >= firstNonPrimary {
return 0
} }
return w return int(ce&primaryValueMask) >> primaryShift
}
func (ce colElem) secondary() int {
switch ce & ceTypeMask {
case ceType1:
return int(uint8(ce))
case ceType2:
return minCompactSecondary + int((ce>>compactSecondaryShift)&0xF)
case ceType3:
return int(uint16(ce >> 8))
case ceTypeQ:
return 0
}
panic("should not reach here")
}
func (ce colElem) tertiary() uint8 {
if ce&hasTertiaryMask == 0 {
if ce&ceType3 == 0 {
return uint8(ce & 0x1F)
}
return uint8(ce)
} else if ce&ceTypeMask == ceType1 {
return defaultTertiary
}
// ce is a quaternary value.
return 0
}
func (ce colElem) updateTertiary(t uint8) colElem {
if ce&ceTypeMask == ceType1 {
nce := ce & primaryValueMask
nce |= colElem(uint8(ce)-minCompactSecondary) << compactSecondaryShift
ce = nce
} else {
ce &= ^colElem(maxTertiary)
}
return ce | colElem(t)
}
// quaternary returns the quaternary value if explicitly specified,
// 0 if ce == ceIgnore, or maxQuaternary otherwise.
// Quaternary values are used only for shifted variants.
func (ce colElem) quaternary() int {
if ce&ceTypeMask == ceTypeQ {
return int(ce&primaryValueMask) >> primaryShift
} else if ce == ceIgnore {
return 0
}
return maxQuaternary
} }
// For contractions, collation elements are of the form // For contractions, collation elements are of the form

View File

@ -29,10 +29,10 @@ func makeCE(weights []int) colElem {
var ce colElem var ce colElem
if weights[0] != 0 { if weights[0] != 0 {
if weights[2] == defaultTertiary { if weights[2] == defaultTertiary {
ce = colElem(weights[0]<<maxSecondaryCompactBits + weights[1]) ce = colElem(weights[0]<<(maxSecondaryCompactBits+1) + weights[1])
ce |= isPrimary ce |= isPrimary
} else { } else {
d := weights[1] - defaultSecondary d := weights[1] - defaultSecondary + 4
ce = colElem(weights[0]<<maxSecondaryDiffBits + d) ce = colElem(weights[0]<<maxSecondaryDiffBits + d)
ce = ce<<maxTertiaryCompactBits + colElem(weights[2]) ce = ce<<maxTertiaryCompactBits + colElem(weights[2])
} }
@ -68,10 +68,10 @@ func makeDecompose(t1, t2 int) colElem {
} }
func normalCE(inout []int) (ce colElem, t ceType) { func normalCE(inout []int) (ce colElem, t ceType) {
w := splitCE(makeCE(inout)) w := makeCE(inout)
inout[0] = int(w.primary) inout[0] = w.primary()
inout[1] = int(w.secondary) inout[1] = w.secondary()
inout[2] = int(w.tertiary) inout[2] = int(w.tertiary())
return ce, ceNormal return ce, ceNormal
} }
@ -167,3 +167,20 @@ func TestImplicit(t *testing.T) {
} }
} }
} }
func TestUpdateTertiary(t *testing.T) {
tests := []struct {
in, out colElem
t uint8
}{
{0x4000FE20, 0x0000FE8A, 0x0A},
{0x4000FE21, 0x0000FEAA, 0x0A},
{0x0000FE8B, 0x0000FE83, 0x03},
{0x8000CC02, 0x8000CC1B, 0x1B},
}
for i, tt := range tests {
if out := tt.in.updateTertiary(tt.t); out != tt.out {
t.Errorf("%d: was %X; want %X", i, out, tt.out)
}
}
}

View File

@ -30,7 +30,7 @@ const (
// AlternateHandling identifies the various ways in which variables are handled. // AlternateHandling identifies the various ways in which variables are handled.
// A rune with a primary weight lower than the variable top is considered a // A rune with a primary weight lower than the variable top is considered a
// variable. // variable.
// See http://www.unicode.org/reports/tr10/#Variable_Weighting for details. // See http://www.unicode.org/reports/tr10/#Variable_Weighting for details.
type AlternateHandling int type AlternateHandling int
@ -83,9 +83,17 @@ type Collator struct {
f norm.Form f norm.Form
t *table t *table
_iter [2]iter
}
func (c *Collator) iter(i int) *iter {
// TODO: evaluate performance for making the second iterator optional.
return &c._iter[i]
} }
// Locales returns the list of locales for which collating differs from its parent locale. // Locales returns the list of locales for which collating differs from its parent locale.
// The returned value should not be modified.
func Locales() []string { func Locales() []string {
return availableLocales return availableLocales
} }
@ -99,11 +107,18 @@ func New(loc string) *Collator {
t = mainTable.indexedTable(idx) t = mainTable.indexedTable(idx)
} }
} }
return &Collator{ return newCollator(t)
}
func newCollator(t *table) *Collator {
c := &Collator{
Strength: Quaternary, Strength: Quaternary,
f: norm.NFD, f: norm.NFD,
t: t, t: t,
} }
c._iter[0].init(c)
c._iter[1].init(c)
return c
} }
// SetVariableTop sets all runes with primary strength less than the primary // SetVariableTop sets all runes with primary strength less than the primary
@ -112,63 +127,114 @@ func (c *Collator) SetVariableTop(r rune) {
// TODO: implement // TODO: implement
} }
// Buffer holds reusable buffers that can be used during collation. // Buffer holds keys generated by Key and KeyString.
// Reusing a Buffer for the various calls that accept it may avoid
// unnecessary memory allocations.
type Buffer struct { type Buffer struct {
// TODO: try various parameters and techniques, such as using buf [4096]byte
// a chan of buffers for a pool.
ba [4096]byte
wa [512]weights
key []byte key []byte
ce []weights
} }
func (b *Buffer) init() { func (b *Buffer) init() {
if b.ce == nil { if b.key == nil {
b.ce = b.wa[:0] b.key = b.buf[:0]
b.key = b.ba[:0]
} else {
b.ce = b.ce[:0]
} }
} }
// ResetKeys clears the buffer used for generated keys. Calling ResetKeys // Reset clears the buffer from previous results generated by Key and KeyString.
// invalidates keys previously obtained from Key or KeyFromString. func (b *Buffer) Reset() {
func (b *Buffer) ResetKeys() {
b.ce = b.ce[:0]
b.key = b.key[:0] b.key = b.key[:0]
} }
// Compare returns an integer comparing the two byte slices. // Compare returns an integer comparing the two byte slices.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// Compare calls ResetKeys, thereby invalidating keys func (c *Collator) Compare(a, b []byte) int {
// previously generated using Key or KeyFromString using buf. // TODO: skip identical prefixes once we have a fast way to detect if a rune is
func (c *Collator) Compare(buf *Buffer, a, b []byte) int { // part of a contraction. This would lead to roughly a 10% speedup for the colcmp regtest.
// TODO: for now we simply compute keys and compare. Once we c.iter(0).setInput(c, a)
// have good benchmarks, move to an implementation that works c.iter(1).setInput(c, b)
// incrementally for the majority of cases. if res := c.compare(); res != 0 {
// - Benchmark with long strings that only vary in modifiers. return res
buf.ResetKeys() }
ka := c.Key(buf, a) if Identity == c.Strength {
kb := c.Key(buf, b) return bytes.Compare(a, b)
defer buf.ResetKeys() }
return bytes.Compare(ka, kb) return 0
} }
// CompareString returns an integer comparing the two strings. // CompareString returns an integer comparing the two strings.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// CompareString calls ResetKeys, thereby invalidating keys func (c *Collator) CompareString(a, b string) int {
// previously generated using Key or KeyFromString using buf. // TODO: skip identical prefixes once we have a fast way to detect if a rune is
func (c *Collator) CompareString(buf *Buffer, a, b string) int { // part of a contraction. This would lead to roughly a 10% speedup for the colcmp regtest.
buf.ResetKeys() c.iter(0).setInputString(c, a)
ka := c.KeyFromString(buf, a) c.iter(1).setInputString(c, b)
kb := c.KeyFromString(buf, b) if res := c.compare(); res != 0 {
defer buf.ResetKeys() return res
return bytes.Compare(ka, kb) }
if Identity == c.Strength {
if a < b {
return -1
} else if a > b {
return 1
}
}
return 0
} }
func (c *Collator) Prefix(buf *Buffer, s, prefix []byte) int { func compareLevel(f func(i *iter) int, a, b *iter) int {
a.pce = 0
b.pce = 0
for {
va := f(a)
vb := f(b)
if va != vb {
if va < vb {
return -1
}
return 1
} else if va == 0 {
break
}
}
return 0
}
func (c *Collator) compare() int {
ia, ib := c.iter(0), c.iter(1)
// Process primary level
if c.Alternate != AltShifted {
// TODO: implement script reordering
// TODO: special hiragana handling
if res := compareLevel((*iter).nextPrimary, ia, ib); res != 0 {
return res
}
} else {
// TODO: handle shifted
}
if Secondary <= c.Strength {
f := (*iter).nextSecondary
if c.Backwards {
f = (*iter).prevSecondary
}
if res := compareLevel(f, ia, ib); res != 0 {
return res
}
}
// TODO: special case handling (Danish?)
if Tertiary <= c.Strength || c.CaseLevel {
if res := compareLevel((*iter).nextTertiary, ia, ib); res != 0 {
return res
}
// TODO: Not needed for the default value of AltNonIgnorable?
if Quaternary <= c.Strength {
if res := compareLevel((*iter).nextQuaternary, ia, ib); res != 0 {
return res
}
}
}
return 0
}
func (c *Collator) Prefix(s, prefix []byte) int {
// iterate over s, track bytes consumed. // iterate over s, track bytes consumed.
return 0 return 0
} }
@ -176,12 +242,11 @@ func (c *Collator) Prefix(buf *Buffer, s, prefix []byte) int {
// Key returns the collation key for str. // Key returns the collation key for str.
// Passing the buffer buf may avoid memory allocations. // Passing the buffer buf may avoid memory allocations.
// The returned slice will point to an allocation in Buffer and will remain // The returned slice will point to an allocation in Buffer and will remain
// valid until the next call to buf.ResetKeys(). // valid until the next call to buf.Reset().
func (c *Collator) Key(buf *Buffer, str []byte) []byte { func (c *Collator) Key(buf *Buffer, str []byte) []byte {
// See http://www.unicode.org/reports/tr10/#Main_Algorithm for more details. // See http://www.unicode.org/reports/tr10/#Main_Algorithm for more details.
buf.init() buf.init()
c.getColElems(buf, str) return c.key(buf, c.getColElems(str))
return c.key(buf, buf.ce)
} }
// KeyFromString returns the collation key for str. // KeyFromString returns the collation key for str.
@ -191,46 +256,73 @@ func (c *Collator) Key(buf *Buffer, str []byte) []byte {
func (c *Collator) KeyFromString(buf *Buffer, str string) []byte { func (c *Collator) KeyFromString(buf *Buffer, str string) []byte {
// See http://www.unicode.org/reports/tr10/#Main_Algorithm for more details. // See http://www.unicode.org/reports/tr10/#Main_Algorithm for more details.
buf.init() buf.init()
c.getColElemsString(buf, str) return c.key(buf, c.getColElemsString(str))
return c.key(buf, buf.ce)
} }
func (c *Collator) key(buf *Buffer, w []weights) []byte { func (c *Collator) key(buf *Buffer, w []colElem) []byte {
processWeights(c.Alternate, c.t.variableTop, w) processWeights(c.Alternate, c.t.variableTop, w)
kn := len(buf.key) kn := len(buf.key)
c.keyFromElems(buf, w) c.keyFromElems(buf, w)
return buf.key[kn:] return buf.key[kn:]
} }
func (c *Collator) getColElems(buf *Buffer, str []byte) { func (c *Collator) getColElems(str []byte) []colElem {
i := c.iter() i := c.iter(0)
i.src.SetInput(c.f, str) i.setInput(c, str)
for !i.done() { for !i.done() {
buf.ce = i.next(buf.ce) i.next()
} }
return i.ce
} }
func (c *Collator) getColElemsString(buf *Buffer, str string) { func (c *Collator) getColElemsString(str string) []colElem {
i := c.iter() i := c.iter(0)
i.src.SetInputString(c.f, str) i.setInputString(c, str)
for !i.done() { for !i.done() {
buf.ce = i.next(buf.ce) i.next()
} }
return i.ce
} }
type iter struct { type iter struct {
src norm.Iter src norm.Iter
ba [1024]byte norm [1024]byte
buf []byte buf []byte
t *table
p int p int
minBufSize int minBufSize int
wa [512]colElem
ce []colElem
pce int
t *table
_done, eof bool _done, eof bool
} }
func (c *Collator) iter() iter { func (i *iter) init(c *Collator) {
i := iter{t: c.t, minBufSize: c.t.maxContractLen} i.t = c.t
i.buf = i.ba[:0] i.minBufSize = c.t.maxContractLen
i.ce = i.wa[:0]
i.buf = i.norm[:0]
}
func (i *iter) reset() {
i.ce = i.ce[:0]
i.buf = i.buf[:0]
i.p = 0
i.eof = i.src.Done()
i._done = i.eof
}
func (i *iter) setInput(c *Collator, s []byte) *iter {
i.src.SetInput(c.f, s)
i.reset()
return i
}
func (i *iter) setInputString(c *Collator, s string) *iter {
i.src.SetInputString(c.f, s)
i.reset()
return i return i
} }
@ -238,7 +330,7 @@ func (i *iter) done() bool {
return i._done return i._done
} }
func (i *iter) next(ce []weights) []weights { func (i *iter) next() {
if !i.eof && len(i.buf)-i.p < i.minBufSize { if !i.eof && len(i.buf)-i.p < i.minBufSize {
// replenish buffer // replenish buffer
n := copy(i.buf, i.buf[i.p:]) n := copy(i.buf, i.buf[i.p:])
@ -249,14 +341,70 @@ func (i *iter) next(ce []weights) []weights {
} }
if i.p == len(i.buf) { if i.p == len(i.buf) {
i._done = true i._done = true
return ce return
} }
ce, sz := i.t.appendNext(ce, i.buf[i.p:]) sz := 0
i.ce, sz = i.t.appendNext(i.ce, i.buf[i.p:])
i.p += sz i.p += sz
return ce
} }
func appendPrimary(key []byte, p uint32) []byte { func (i *iter) nextPrimary() int {
for {
for ; i.pce < len(i.ce); i.pce++ {
if v := i.ce[i.pce].primary(); v != 0 {
i.pce++
return v
}
}
if i.done() {
return 0
}
i.next()
}
panic("should not reach here")
}
func (i *iter) nextSecondary() int {
for ; i.pce < len(i.ce); i.pce++ {
if v := i.ce[i.pce].secondary(); v != 0 {
i.pce++
return v
}
}
return 0
}
func (i *iter) prevSecondary() int {
for ; i.pce < len(i.ce); i.pce++ {
if v := i.ce[len(i.ce)-i.pce-1].secondary(); v != 0 {
i.pce++
return v
}
}
return 0
}
func (i *iter) nextTertiary() int {
for ; i.pce < len(i.ce); i.pce++ {
if v := i.ce[i.pce].tertiary(); v != 0 {
i.pce++
return int(v)
}
}
return 0
}
func (i *iter) nextQuaternary() int {
for ; i.pce < len(i.ce); i.pce++ {
if v := i.ce[i.pce].quaternary(); v != 0 {
i.pce++
return v
}
}
return 0
}
func appendPrimary(key []byte, p int) []byte {
// Convert to variable length encoding; supports up to 23 bits. // Convert to variable length encoding; supports up to 23 bits.
if p <= 0x7FFF { if p <= 0x7FFF {
key = append(key, uint8(p>>8), uint8(p)) key = append(key, uint8(p>>8), uint8(p))
@ -268,9 +416,9 @@ func appendPrimary(key []byte, p uint32) []byte {
// keyFromElems converts the weights ws to a compact sequence of bytes. // keyFromElems converts the weights ws to a compact sequence of bytes.
// The result will be appended to the byte buffer in buf. // The result will be appended to the byte buffer in buf.
func (c *Collator) keyFromElems(buf *Buffer, ws []weights) { func (c *Collator) keyFromElems(buf *Buffer, ws []colElem) {
for _, v := range ws { for _, v := range ws {
if w := v.primary; w > 0 { if w := v.primary(); w > 0 {
buf.key = appendPrimary(buf.key, w) buf.key = appendPrimary(buf.key, w)
} }
} }
@ -279,13 +427,13 @@ func (c *Collator) keyFromElems(buf *Buffer, ws []weights) {
// TODO: we can use one 0 if we can guarantee that all non-zero weights are > 0xFF. // TODO: we can use one 0 if we can guarantee that all non-zero weights are > 0xFF.
if !c.Backwards { if !c.Backwards {
for _, v := range ws { for _, v := range ws {
if w := v.secondary; w > 0 { if w := v.secondary(); w > 0 {
buf.key = append(buf.key, uint8(w>>8), uint8(w)) buf.key = append(buf.key, uint8(w>>8), uint8(w))
} }
} }
} else { } else {
for i := len(ws) - 1; i >= 0; i-- { for i := len(ws) - 1; i >= 0; i-- {
if w := ws[i].secondary; w > 0 { if w := ws[i].secondary(); w > 0 {
buf.key = append(buf.key, uint8(w>>8), uint8(w)) buf.key = append(buf.key, uint8(w>>8), uint8(w))
} }
} }
@ -296,20 +444,20 @@ func (c *Collator) keyFromElems(buf *Buffer, ws []weights) {
if Tertiary <= c.Strength || c.CaseLevel { if Tertiary <= c.Strength || c.CaseLevel {
buf.key = append(buf.key, 0, 0) buf.key = append(buf.key, 0, 0)
for _, v := range ws { for _, v := range ws {
if w := v.tertiary; w > 0 { if w := v.tertiary(); w > 0 {
buf.key = append(buf.key, w) buf.key = append(buf.key, uint8(w))
} }
} }
// Derive the quaternary weights from the options and other levels. // Derive the quaternary weights from the options and other levels.
// Note that we represent maxQuaternary as 0xFF. The first byte of the // Note that we represent maxQuaternary as 0xFF. The first byte of the
// representation of a a primary weight is always smaller than 0xFF, // representation of a a primary weight is always smaller than 0xFF,
// so using this single byte value will compare correctly. // so using this single byte value will compare correctly.
if Quaternary <= c.Strength { if Quaternary <= c.Strength && c.Alternate >= AltShifted {
if c.Alternate == AltShiftTrimmed { if c.Alternate == AltShiftTrimmed {
lastNonFFFF := len(buf.key) lastNonFFFF := len(buf.key)
buf.key = append(buf.key, 0) buf.key = append(buf.key, 0)
for _, v := range ws { for _, v := range ws {
if w := v.quaternary; w == maxQuaternary { if w := v.quaternary(); w == maxQuaternary {
buf.key = append(buf.key, 0xFF) buf.key = append(buf.key, 0xFF)
} else if w > 0 { } else if w > 0 {
buf.key = appendPrimary(buf.key, w) buf.key = appendPrimary(buf.key, w)
@ -320,7 +468,7 @@ func (c *Collator) keyFromElems(buf *Buffer, ws []weights) {
} else { } else {
buf.key = append(buf.key, 0) buf.key = append(buf.key, 0)
for _, v := range ws { for _, v := range ws {
if w := v.quaternary; w == maxQuaternary { if w := v.quaternary(); w == maxQuaternary {
buf.key = append(buf.key, 0xFF) buf.key = append(buf.key, 0xFF)
} else if w > 0 { } else if w > 0 {
buf.key = appendPrimary(buf.key, w) buf.key = appendPrimary(buf.key, w)
@ -331,29 +479,27 @@ func (c *Collator) keyFromElems(buf *Buffer, ws []weights) {
} }
} }
func processWeights(vw AlternateHandling, top uint32, wa []weights) { func processWeights(vw AlternateHandling, top uint32, wa []colElem) {
ignore := false ignore := false
vtop := int(top)
switch vw { switch vw {
case AltShifted, AltShiftTrimmed: case AltShifted, AltShiftTrimmed:
for i := range wa { for i := range wa {
if p := wa[i].primary; p <= top && p != 0 { if p := wa[i].primary(); p <= vtop && p != 0 {
wa[i] = weights{quaternary: p} wa[i] = makeQuaternary(p)
ignore = true ignore = true
} else if p == 0 { } else if p == 0 {
if ignore { if ignore {
wa[i] = weights{} wa[i] = ceIgnore
} else if wa[i].tertiary != 0 {
wa[i].quaternary = maxQuaternary
} }
} else { } else {
wa[i].quaternary = maxQuaternary
ignore = false ignore = false
} }
} }
case AltBlanked: case AltBlanked:
for i := range wa { for i := range wa {
if p := wa[i].primary; p <= top && (ignore || p != 0) { if p := wa[i].primary(); p <= vtop && (ignore || p != 0) {
wa[i] = weights{} wa[i] = ceIgnore
ignore = true ignore = true
} else { } else {
ignore = false ignore = false

View File

@ -4,8 +4,6 @@
package collate package collate
import "exp/norm"
// Init is used by type Builder in exp/locale/collate/build/ // Init is used by type Builder in exp/locale/collate/build/
// to create Collator instances. It is for internal use only. // to create Collator instances. It is for internal use only.
func Init(data interface{}) *Collator { func Init(data interface{}) *Collator {
@ -24,11 +22,7 @@ func Init(data interface{}) *Collator {
t.contractElem = init.ContractElems() t.contractElem = init.ContractElems()
t.maxContractLen = init.MaxContractLen() t.maxContractLen = init.MaxContractLen()
t.variableTop = init.VariableTop() t.variableTop = init.VariableTop()
return &Collator{ return newCollator(t)
Strength: Quaternary,
f: norm.NFD,
t: t,
}
} }
type tableInitializer interface { type tableInitializer interface {

View File

@ -24,6 +24,8 @@ func W(ce ...int) Weights {
} }
if len(ce) > 3 { if len(ce) > 3 {
w.Quaternary = ce[3] w.Quaternary = ce[3]
} else if w.Tertiary != 0 {
w.Quaternary = maxQuaternary
} }
return w return w
} }
@ -33,25 +35,27 @@ func (w Weights) String() string {
type Table struct { type Table struct {
t *table t *table
w []weights
} }
func GetTable(c *Collator) *Table { func GetTable(c *Collator) *Table {
return &Table{c.t, nil} return &Table{c.t}
} }
func convertToWeights(ws []weights) []Weights { func convertToWeights(ws []colElem) []Weights {
out := make([]Weights, len(ws)) out := make([]Weights, len(ws))
for i, w := range ws { for i, w := range ws {
out[i] = Weights{int(w.primary), int(w.secondary), int(w.tertiary), int(w.quaternary)} out[i] = Weights{int(w.primary()), int(w.secondary()), int(w.tertiary()), int(w.quaternary())}
} }
return out return out
} }
func convertFromWeights(ws []Weights) []weights { func convertFromWeights(ws []Weights) []colElem {
out := make([]weights, len(ws)) out := make([]colElem, len(ws))
for i, w := range ws { for i, w := range ws {
out[i] = weights{uint32(w.Primary), uint16(w.Secondary), uint8(w.Tertiary), uint32(w.Quaternary)} out[i] = makeCE([]int{w.Primary, w.Secondary, w.Tertiary})
if out[i] == ceIgnore && w.Quaternary > 0 {
out[i] = makeQuaternary(w.Quaternary)
}
} }
return out return out
} }
@ -68,10 +72,9 @@ func SetTop(c *Collator, top int) {
c.t.variableTop = uint32(top) c.t.variableTop = uint32(top)
} }
func GetColElems(c *Collator, buf *Buffer, str []byte) []Weights { func GetColElems(c *Collator, str []byte) []Weights {
buf.ResetKeys() ce := c.getColElems(str)
c.getColElems(buf, str) return convertToWeights(ce)
return convertToWeights(buf.ce)
} }
func ProcessWeights(h AlternateHandling, top int, w []Weights) []Weights { func ProcessWeights(h AlternateHandling, top int, w []Weights) []Weights {

View File

@ -38,7 +38,7 @@ var (
`URL of the Default Unicode Collation Element Table (DUCET). This can be a zip `URL of the Default Unicode Collation Element Table (DUCET). This can be a zip
file containing the file allkeys_CLDR.txt or an allkeys.txt file.`) file containing the file allkeys_CLDR.txt or an allkeys.txt file.`)
cldr = flag.String("cldr", cldr = flag.String("cldr",
"http://www.unicode.org/Public/cldr/2.0.1/core.zip", "http://www.unicode.org/Public/cldr/22/core.zip",
"URL of CLDR archive.") "URL of CLDR archive.")
test = flag.Bool("test", false, test = flag.Bool("test", false,
"test existing tables; can be used to compare web data with package data.") "test existing tables; can be used to compare web data with package data.")
@ -180,7 +180,7 @@ func skipAlt(a string) bool {
func failOnError(e error) { func failOnError(e error) {
if e != nil { if e != nil {
log.Fatal(e) log.Panic(e)
} }
} }
@ -607,7 +607,7 @@ func insertTailoring(t *build.Tailoring, r RuleElem, context, extend string) {
if *test { if *test {
testInput.add(str) testInput.add(str)
} }
err := t.Insert(lmap[l[0]], str, extend) err := t.Insert(lmap[l[0]], str, context+extend)
failOnError(err) failOnError(err)
} }
case "pc", "sc", "tc", "ic": case "pc", "sc", "tc", "ic":
@ -617,7 +617,7 @@ func insertTailoring(t *build.Tailoring, r RuleElem, context, extend string) {
if *test { if *test {
testInput.add(str) testInput.add(str)
} }
err := t.Insert(level, str, extend) err := t.Insert(level, str, context+extend)
failOnError(err) failOnError(err)
} }
default: default:
@ -677,7 +677,7 @@ func testCollator(c *collate.Collator) {
if bytes.Compare(k0, k) != 0 { if bytes.Compare(k0, k) != 0 {
failOnError(fmt.Errorf("test:%U: keys differ (%x vs %x)", []rune(str), k0, k)) failOnError(fmt.Errorf("test:%U: keys differ (%x vs %x)", []rune(str), k0, k))
} }
buf.ResetKeys() buf.Reset()
} }
fmt.Println("PASS") fmt.Println("PASS")
} }

View File

@ -236,9 +236,9 @@ func doTest(t Test) {
if strings.Contains(t.name, "NON_IGNOR") { if strings.Contains(t.name, "NON_IGNOR") {
c.Alternate = collate.AltNonIgnorable c.Alternate = collate.AltNonIgnorable
} }
prev := t.str[0] prev := t.str[0]
for i := 1; i < len(t.str); i++ { for i := 1; i < len(t.str); i++ {
b.Reset()
s := t.str[i] s := t.str[i]
ka := c.Key(b, prev) ka := c.Key(b, prev)
kb := c.Key(b, s) kb := c.Key(b, s)
@ -247,10 +247,10 @@ func doTest(t Test) {
prev = s prev = s
continue continue
} }
if r := c.Compare(b, prev, s); r == 1 { if r := c.Compare(prev, s); r == 1 {
fail(t, "%d: Compare(%.4X, %.4X) == %d; want -1 or 0", i, runes(prev), runes(s), r) fail(t, "%d: Compare(%.4X, %.4X) == %d; want -1 or 0", i, runes(prev), runes(s), r)
} }
if r := c.Compare(b, s, prev); r == -1 { if r := c.Compare(s, prev); r == -1 {
fail(t, "%d: Compare(%.4X, %.4X) == %d; want 1 or 0", i, runes(s), runes(prev), r) fail(t, "%d: Compare(%.4X, %.4X) == %d; want 1 or 0", i, runes(s), runes(prev), r)
} }
prev = s prev = s

View File

@ -37,17 +37,21 @@ func (t *table) indexedTable(idx tableIndex) *table {
return &nt return &nt
} }
// appendNext appends the weights corresponding to the next rune or // appendNext appends the weights corresponding to the next rune or
// contraction in s. If a contraction is matched to a discontinuous // contraction in s. If a contraction is matched to a discontinuous
// sequence of runes, the weights for the interstitial runes are // sequence of runes, the weights for the interstitial runes are
// appended as well. It returns a new slice that includes the appended // appended as well. It returns a new slice that includes the appended
// weights and the number of bytes consumed from s. // weights and the number of bytes consumed from s.
func (t *table) appendNext(w []weights, s []byte) ([]weights, int) { func (t *table) appendNext(w []colElem, s []byte) ([]colElem, int) {
v, sz := t.index.lookup(s) v, sz := t.index.lookup(s)
ce := colElem(v) ce := colElem(v)
tp := ce.ctype() tp := ce.ctype()
if tp == ceNormal { if tp == ceNormal {
w = append(w, getWeights(ce, s)) if ce == 0 {
r, _ := utf8.DecodeRune(s)
ce = makeImplicitCE(implicitPrimary(r))
}
w = append(w, ce)
} else if tp == ceExpansionIndex { } else if tp == ceExpansionIndex {
w = t.appendExpansion(w, ce) w = t.appendExpansion(w, ce)
} else if tp == ceContractionIndex { } else if tp == ceContractionIndex {
@ -62,40 +66,28 @@ func (t *table) appendNext(w []weights, s []byte) ([]weights, int) {
for p := 0; len(nfkd) > 0; nfkd = nfkd[p:] { for p := 0; len(nfkd) > 0; nfkd = nfkd[p:] {
w, p = t.appendNext(w, nfkd) w, p = t.appendNext(w, nfkd)
} }
w[i].tertiary = t1 w[i] = w[i].updateTertiary(t1)
if i++; i < len(w) { if i++; i < len(w) {
w[i].tertiary = t2 w[i] = w[i].updateTertiary(t2)
for i++; i < len(w); i++ { for i++; i < len(w); i++ {
w[i].tertiary = maxTertiary w[i] = w[i].updateTertiary(maxTertiary)
} }
} }
} }
return w, sz return w, sz
} }
func getWeights(ce colElem, s []byte) weights { func (t *table) appendExpansion(w []colElem, ce colElem) []colElem {
if ce == 0 { // implicit
r, _ := utf8.DecodeRune(s)
return weights{
primary: uint32(implicitPrimary(r)),
secondary: defaultSecondary,
tertiary: defaultTertiary,
}
}
return splitCE(ce)
}
func (t *table) appendExpansion(w []weights, ce colElem) []weights {
i := splitExpandIndex(ce) i := splitExpandIndex(ce)
n := int(t.expandElem[i]) n := int(t.expandElem[i])
i++ i++
for _, ce := range t.expandElem[i : i+n] { for _, ce := range t.expandElem[i : i+n] {
w = append(w, splitCE(colElem(ce))) w = append(w, colElem(ce))
} }
return w return w
} }
func (t *table) matchContraction(w []weights, ce colElem, suffix []byte) ([]weights, int) { func (t *table) matchContraction(w []colElem, ce colElem, suffix []byte) ([]colElem, int) {
index, n, offset := splitContractIndex(ce) index, n, offset := splitContractIndex(ce)
scan := t.contractTries.scanner(index, n, suffix) scan := t.contractTries.scanner(index, n, suffix)
@ -138,7 +130,7 @@ func (t *table) matchContraction(w []weights, ce colElem, suffix []byte) ([]weig
i, n := scan.result() i, n := scan.result()
ce = colElem(t.contractElem[i+offset]) ce = colElem(t.contractElem[i+offset])
if ce.ctype() == ceNormal { if ce.ctype() == ceNormal {
w = append(w, splitCE(ce)) w = append(w, ce)
} else { } else {
w = t.appendExpansion(w, ce) w = t.appendExpansion(w, ce)
} }

File diff suppressed because it is too large Load Diff

View File

@ -91,5 +91,5 @@ func (c *goCollator) Key(b Input) []byte {
} }
func (c *goCollator) Compare(a, b Input) int { func (c *goCollator) Compare(a, b Input) int {
return c.c.Compare(&c.buf, a.UTF8, b.UTF8) return c.c.Compare(a.UTF8, b.UTF8)
} }

View File

@ -399,7 +399,7 @@ var cmdRegress = &Command{
} }
const failedKeyCompare = ` const failedKeyCompare = `
%d: incorrect comparison result for input: %s:%d: incorrect comparison result for input:
a: %q (%.4X) a: %q (%.4X)
key: %s key: %s
b: %q (%.4X) b: %q (%.4X)
@ -412,7 +412,7 @@ const failedKeyCompare = `
` `
const failedCompare = ` const failedCompare = `
%d: incorrect comparison result for input: %s:%d: incorrect comparison result for input:
a: %q (%.4X) a: %q (%.4X)
b: %q (%.4X) b: %q (%.4X)
Compare(a, b) = %d; want %d. Compare(a, b) = %d; want %d.
@ -453,12 +453,12 @@ func runRegress(ctxt *Context, args []string) {
count++ count++
a := string(ia.UTF8) a := string(ia.UTF8)
b := string(ib.UTF8) b := string(ib.UTF8)
fmt.Printf(failedKeyCompare, i-1, a, []rune(a), keyStr(ia.key), b, []rune(b), keyStr(ib.key), cmp, goldCmp, keyStr(gold.Key(ia)), keyStr(gold.Key(ib))) fmt.Printf(failedKeyCompare, t.Locale, i-1, a, []rune(a), keyStr(ia.key), b, []rune(b), keyStr(ib.key), cmp, goldCmp, keyStr(gold.Key(ia)), keyStr(gold.Key(ib)))
} else if cmp := t.Col.Compare(ia, ib); cmp != goldCmp { } else if cmp := t.Col.Compare(ia, ib); cmp != goldCmp {
count++ count++
a := string(ia.UTF8) a := string(ia.UTF8)
b := string(ib.UTF8) b := string(ib.UTF8)
fmt.Printf(failedKeyCompare, i-1, a, []rune(a), b, []rune(b), cmp, goldCmp) fmt.Printf(failedCompare, t.Locale, i-1, a, []rune(a), b, []rune(b), cmp, goldCmp)
} }
} }
if count > 0 { if count > 0 {

View File

@ -4,7 +4,7 @@
// The trie in this file is used to associate the first full character // The trie in this file is used to associate the first full character
// in an UTF-8 string to a collation element. // in an UTF-8 string to a collation element.
// All but the last byte in a UTF-8 byte sequence are // All but the last byte in a UTF-8 byte sequence are
// used to lookup offsets in the index table to be used for the next byte. // used to lookup offsets in the index table to be used for the next byte.
// The last byte is used to index into a table of collation elements. // The last byte is used to index into a table of collation elements.
// For a full description, see exp/locale/collate/build/trie.go. // For a full description, see exp/locale/collate/build/trie.go.

View File

@ -8,7 +8,7 @@ import (
"testing" "testing"
) )
// We take the smallest, largest and an arbitrary value for each // We take the smallest, largest and an arbitrary value for each
// of the UTF-8 sequence lengths. // of the UTF-8 sequence lengths.
var testRunes = []rune{ var testRunes = []rune{
0x01, 0x0C, 0x7F, // 1-byte sequences 0x01, 0x0C, 0x7F, // 1-byte sequences

View File

@ -76,7 +76,7 @@ func init() {
// We do not distinguish between boundaries for NFC, NFD, etc. to avoid // We do not distinguish between boundaries for NFC, NFD, etc. to avoid
// unexpected behavior for the user. For example, in NFD, there is a boundary // unexpected behavior for the user. For example, in NFD, there is a boundary
// after 'a'. However, 'a' might combine with modifiers, so from the application's // after 'a'. However, 'a' might combine with modifiers, so from the application's
// perspective it is not a good boundary. We will therefore always use the // perspective it is not a good boundary. We will therefore always use the
// boundaries for the combining variants. // boundaries for the combining variants.
// BoundaryBefore returns true if this rune starts a new segment and // BoundaryBefore returns true if this rune starts a new segment and
@ -101,7 +101,7 @@ func (p Properties) BoundaryAfter() bool {
// 0: NFD_QC Yes (0) or No (1). No also means there is a decomposition. // 0: NFD_QC Yes (0) or No (1). No also means there is a decomposition.
// 1..2: NFC_QC Yes(00), No (10), or Maybe (11) // 1..2: NFC_QC Yes(00), No (10), or Maybe (11)
// 3: Combines forward (0 == false, 1 == true) // 3: Combines forward (0 == false, 1 == true)
// //
// When all 4 bits are zero, the character is inert, meaning it is never // When all 4 bits are zero, the character is inert, meaning it is never
// influenced by normalization. // influenced by normalization.
type qcInfo uint8 type qcInfo uint8

View File

@ -64,9 +64,9 @@ func (i *Iter) Done() bool {
} }
// Next writes f(i.input[i.Pos():n]...) to buffer buf, where n is the // Next writes f(i.input[i.Pos():n]...) to buffer buf, where n is the
// largest boundary of i.input such that the result fits in buf. // largest boundary of i.input such that the result fits in buf.
// It returns the number of bytes written to buf. // It returns the number of bytes written to buf.
// len(buf) should be at least MaxSegmentSize. // len(buf) should be at least MaxSegmentSize.
// Done must be false before calling Next. // Done must be false before calling Next.
func (i *Iter) Next(buf []byte) int { func (i *Iter) Next(buf []byte) int {
return i.next(i, buf) return i.next(i, buf)

View File

@ -86,7 +86,7 @@ const (
// Quick Check properties of runes allow us to quickly // Quick Check properties of runes allow us to quickly
// determine whether a rune may occur in a normal form. // determine whether a rune may occur in a normal form.
// For a given normal form, a rune may be guaranteed to occur // For a given normal form, a rune may be guaranteed to occur
// verbatim (QC=Yes), may or may not combine with another // verbatim (QC=Yes), may or may not combine with another
// rune (QC=Maybe), or may not occur (QC=No). // rune (QC=Maybe), or may not occur (QC=No).
type QCResult int type QCResult int

View File

@ -16,7 +16,7 @@ func main() {
printTestTables() printTestTables()
} }
// We take the smallest, largest and an arbitrary value for each // We take the smallest, largest and an arbitrary value for each
// of the UTF-8 sequence lengths. // of the UTF-8 sequence lengths.
var testRunes = []rune{ var testRunes = []rune{
0x01, 0x0C, 0x7F, // 1-byte sequences 0x01, 0x0C, 0x7F, // 1-byte sequences

View File

@ -57,7 +57,7 @@ var logger = log.New(os.Stderr, "", log.Lshortfile)
// 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE // 1E0A;1E0A;0044 0307;1E0A;0044 0307; # (Ḋ; Ḋ; D◌̇; Ḋ; D◌̇; ) LATIN CAPITAL LETTER D WITH DOT ABOVE
// 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW // 1E0C;1E0C;0044 0323;1E0C;0044 0323; # (Ḍ; Ḍ; D◌̣; Ḍ; D◌̣; ) LATIN CAPITAL LETTER D WITH DOT BELOW
// //
// Each test has 5 columns (c1, c2, c3, c4, c5), where // Each test has 5 columns (c1, c2, c3, c4, c5), where
// (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1)) // (c1, c2, c3, c4, c5) == (c1, NFC(c1), NFD(c1), NFKC(c1), NFKD(c1))
// //
// CONFORMANCE: // CONFORMANCE:

File diff suppressed because it is too large Load Diff

View File

@ -44,7 +44,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
switch id { switch id {
case _Make, _New: case _Make, _New:
// argument must be a type // argument must be a type
typ0 = underlying(check.typ(arg0, false)) typ0 = check.typ(arg0, false)
if typ0 == Typ[Invalid] { if typ0 == Typ[Invalid] {
goto Error goto Error
} }
@ -191,7 +191,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
case _Make: case _Make:
var min int // minimum number of arguments var min int // minimum number of arguments
switch typ0.(type) { switch underlying(typ0).(type) {
case *Slice: case *Slice:
min = 2 min = 2
case *Map, *Chan: case *Map, *Chan:
@ -204,13 +204,27 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n) check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, n)
goto Error goto Error
} }
var sizes []interface{} // constant integer arguments, if any
for _, arg := range args[1:] { for _, arg := range args[1:] {
check.expr(x, arg, nil, iota) check.expr(x, arg, nil, iota)
if !x.isInteger() { if x.isInteger() {
if x.mode == constant {
if isNegConst(x.val) {
check.invalidArg(x.pos(), "%s must not be negative", x)
// safe to continue
} else {
sizes = append(sizes, x.val) // x.val >= 0
}
}
} else {
check.invalidArg(x.pos(), "%s must be an integer", x) check.invalidArg(x.pos(), "%s must be an integer", x)
// safe to continue // safe to continue
} }
} }
if len(sizes) == 2 && compareConst(sizes[0], sizes[1], token.GTR) {
check.invalidArg(args[1].Pos(), "length and capacity swapped")
// safe to continue
}
x.mode = variable x.mode = variable
x.typ = typ0 x.typ = typ0
@ -287,7 +301,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *builtin, iota
var t operand var t operand
x1 := x x1 := x
for _, arg := range args { for _, arg := range args {
check.exprOrType(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T)) check.rawExpr(x1, arg, nil, iota, true) // permit trace for types, e.g.: new(trace(T))
check.dump("%s: %s", x1.pos(), x1) check.dump("%s: %s", x1.pos(), x1)
x1 = &t // use incoming x only for first argument x1 = &t // use incoming x only for first argument
} }

View File

@ -9,244 +9,366 @@ package types
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"go/scanner"
"go/token" "go/token"
"strconv" "sort"
) )
const debug = false // enable for debugging
const trace = false
type checker struct { type checker struct {
fset *token.FileSet fset *token.FileSet
errors scanner.ErrorList pkg *ast.Package
types map[ast.Expr]Type errh func(token.Pos, string)
mapf func(ast.Expr, Type)
// lazily initialized
firsterr error
filenames []string // sorted list of package file names for reproducible iteration order
initexprs map[*ast.ValueSpec][]ast.Expr // "inherited" initialization expressions for constant declarations
functypes []*Signature // stack of function signatures; actively typechecked function on top
pos []token.Pos // stack of expr positions; debugging support, used if trace is set
} }
func (c *checker) errorf(pos token.Pos, format string, args ...interface{}) string { // declare declares an object of the given kind and name (ident) in scope;
msg := fmt.Sprintf(format, args...) // decl is the corresponding declaration in the AST. An error is reported
c.errors.Add(c.fset.Position(pos), msg) // if the object was declared before.
return msg
}
// collectFields collects struct fields tok = token.STRUCT), interface methods
// (tok = token.INTERFACE), and function arguments/results (tok = token.FUNC).
// //
func (c *checker) collectFields(tok token.Token, list *ast.FieldList, cycleOk bool) (fields ObjList, tags []string, isVariadic bool) { // TODO(gri) This is very similar to the declare function in go/parser; it
if list != nil { // is only used to associate methods with their respective receiver base types.
for _, field := range list.List { // In a future version, it might be simpler and cleaner do to all the resolution
ftype := field.Type // in the type-checking phase. It would simplify the parser, AST, and also
if t, ok := ftype.(*ast.Ellipsis); ok { // reduce some amount of code duplication.
ftype = t.Elt
isVariadic = true
}
typ := c.makeType(ftype, cycleOk)
tag := ""
if field.Tag != nil {
assert(field.Tag.Kind == token.STRING)
tag, _ = strconv.Unquote(field.Tag.Value)
}
if len(field.Names) > 0 {
// named fields
for _, name := range field.Names {
obj := name.Obj
obj.Type = typ
fields = append(fields, obj)
if tok == token.STRUCT {
tags = append(tags, tag)
}
}
} else {
// anonymous field
switch tok {
case token.STRUCT:
tags = append(tags, tag)
fallthrough
case token.FUNC:
obj := ast.NewObj(ast.Var, "")
obj.Type = typ
fields = append(fields, obj)
case token.INTERFACE:
utyp := Underlying(typ)
if typ, ok := utyp.(*Interface); ok {
// TODO(gri) This is not good enough. Check for double declarations!
fields = append(fields, typ.Methods...)
} else if _, ok := utyp.(*Bad); !ok {
// if utyp is Bad, don't complain (the root cause was reported before)
c.errorf(ftype.Pos(), "interface contains embedded non-interface type")
}
default:
panic("unreachable")
}
}
}
}
return
}
// makeType makes a new type for an AST type specification x or returns
// the type referred to by a type name x. If cycleOk is set, a type may
// refer to itself directly or indirectly; otherwise cycles are errors.
// //
func (c *checker) makeType(x ast.Expr, cycleOk bool) (typ Type) { func (check *checker) declare(scope *ast.Scope, kind ast.ObjKind, ident *ast.Ident, decl ast.Decl) {
if debug { assert(ident.Obj == nil) // identifier already declared or resolved
fmt.Printf("makeType (cycleOk = %v)\n", cycleOk) obj := ast.NewObj(kind, ident.Name)
ast.Print(c.fset, x) obj.Decl = decl
defer func() { ident.Obj = obj
fmt.Printf("-> %T %v\n\n", typ, typ) if ident.Name != "_" {
}() if alt := scope.Insert(obj); alt != nil {
} prevDecl := ""
if pos := alt.Pos(); pos.IsValid() {
switch t := x.(type) { prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos))
case *ast.BadExpr:
return &Bad{}
case *ast.Ident:
// type name
obj := t.Obj
if obj == nil {
// unresolved identifier (error has been reported before)
return &Bad{Msg: fmt.Sprintf("%s is unresolved", t.Name)}
}
if obj.Kind != ast.Typ {
msg := c.errorf(t.Pos(), "%s is not a type", t.Name)
return &Bad{Msg: msg}
}
c.checkObj(obj, cycleOk)
if !cycleOk && obj.Type.(*Name).Underlying == nil {
msg := c.errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
return &Bad{Msg: msg}
}
return obj.Type.(Type)
case *ast.ParenExpr:
return c.makeType(t.X, cycleOk)
case *ast.SelectorExpr:
// qualified identifier
// TODO (gri) eventually, this code belongs to expression
// type checking - here for the time being
if ident, ok := t.X.(*ast.Ident); ok {
if obj := ident.Obj; obj != nil {
if obj.Kind != ast.Pkg {
msg := c.errorf(ident.Pos(), "%s is not a package", obj.Name)
return &Bad{Msg: msg}
}
// TODO(gri) we have a package name but don't
// have the mapping from package name to package
// scope anymore (created in ast.NewPackage).
return &Bad{} // for now
} }
check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
} }
// TODO(gri) can this really happen (the parser should have excluded this)?
msg := c.errorf(t.Pos(), "expected qualified identifier")
return &Bad{Msg: msg}
case *ast.StarExpr:
return &Pointer{Base: c.makeType(t.X, true)}
case *ast.ArrayType:
if t.Len != nil {
// TODO(gri) compute length
return &Array{Elt: c.makeType(t.Elt, cycleOk)}
}
return &Slice{Elt: c.makeType(t.Elt, true)}
case *ast.StructType:
fields, tags, _ := c.collectFields(token.STRUCT, t.Fields, cycleOk)
return &Struct{Fields: fields, Tags: tags}
case *ast.FuncType:
params, _, isVariadic := c.collectFields(token.FUNC, t.Params, true)
results, _, _ := c.collectFields(token.FUNC, t.Results, true)
return &Func{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic}
case *ast.InterfaceType:
methods, _, _ := c.collectFields(token.INTERFACE, t.Methods, cycleOk)
methods.Sort()
return &Interface{Methods: methods}
case *ast.MapType:
return &Map{Key: c.makeType(t.Key, true), Elt: c.makeType(t.Value, true)}
case *ast.ChanType:
return &Chan{Dir: t.Dir, Elt: c.makeType(t.Value, true)}
} }
panic(fmt.Sprintf("unreachable (%T)", x))
} }
// checkObj type checks an object. func (check *checker) valueSpec(pos token.Pos, obj *ast.Object, lhs []*ast.Ident, typ ast.Expr, rhs []ast.Expr, iota int) {
func (c *checker) checkObj(obj *ast.Object, ref bool) { if len(lhs) == 0 {
if obj.Type != nil { check.invalidAST(pos, "missing lhs in declaration")
// object has already been type checked
return return
} }
switch obj.Kind { // determine type for all of lhs, if any
case ast.Bad: // (but only set it for the object we typecheck!)
// ignore var t Type
if typ != nil {
t = check.typ(typ, false)
}
case ast.Con: // len(lhs) > 0
// TODO(gri) complete this if len(lhs) == len(rhs) {
// check only lhs and rhs corresponding to obj
var l, r ast.Expr
for i, name := range lhs {
if name.Obj == obj {
l = lhs[i]
r = rhs[i]
break
}
}
assert(l != nil)
obj.Type = t
check.assign1to1(l, r, nil, true, iota)
return
}
// there must be a type or initialization expressions
if t == nil && len(rhs) == 0 {
check.invalidAST(pos, "missing type or initialization expression")
t = Typ[Invalid]
}
// if we have a type, mark all of lhs
if t != nil {
for _, name := range lhs {
name.Obj.Type = t
}
}
// check initial values, if any
if len(rhs) > 0 {
// TODO(gri) should try to avoid this conversion
lhx := make([]ast.Expr, len(lhs))
for i, e := range lhs {
lhx[i] = e
}
check.assignNtoM(lhx, rhs, true, iota)
}
}
func (check *checker) function(typ *Signature, body *ast.BlockStmt) {
check.functypes = append(check.functypes, typ)
check.stmt(body)
check.functypes = check.functypes[0 : len(check.functypes)-1]
}
// object typechecks an object by assigning it a type; obj.Type must be nil.
// Callers must check obj.Type before calling object; this eliminates a call
// for each identifier that has been typechecked already, a common scenario.
//
func (check *checker) object(obj *ast.Object, cycleOk bool) {
assert(obj.Type == nil)
switch obj.Kind {
case ast.Bad, ast.Pkg:
// nothing to do
case ast.Con, ast.Var:
// The obj.Data field for constants and variables is initialized
// to the respective (hypothetical, for variables) iota value by
// the parser. The object's fields can be in one of the following
// states:
// Type != nil => the constant value is Data
// Type == nil => the object is not typechecked yet, and Data can be:
// Data is int => Data is the value of iota for this declaration
// Data == nil => the object's expression is being evaluated
if obj.Data == nil {
check.errorf(obj.Pos(), "illegal cycle in initialization of %s", obj.Name)
obj.Type = Typ[Invalid]
return
}
spec := obj.Decl.(*ast.ValueSpec)
iota := obj.Data.(int)
obj.Data = nil
// determine initialization expressions
values := spec.Values
if len(values) == 0 && obj.Kind == ast.Con {
values = check.initexprs[spec]
}
check.valueSpec(spec.Pos(), obj, spec.Names, spec.Type, values, iota)
case ast.Typ: case ast.Typ:
typ := &Name{Obj: obj} typ := &NamedType{Obj: obj}
obj.Type = typ // "mark" object so recursion terminates obj.Type = typ // "mark" object so recursion terminates
typ.Underlying = Underlying(c.makeType(obj.Decl.(*ast.TypeSpec).Type, ref)) typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
// typecheck associated method signatures
case ast.Var: if obj.Data != nil {
// TODO(gri) complete this scope := obj.Data.(*ast.Scope)
switch t := typ.Underlying.(type) {
case *Struct:
// struct fields must not conflict with methods
for _, f := range t.Fields {
if m := scope.Lookup(f.Name); m != nil {
check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name)
}
}
// ok to continue
case *Interface:
// methods cannot be associated with an interface type
for _, m := range scope.Objects {
recv := m.Decl.(*ast.FuncDecl).Recv.List[0].Type
check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.Name, obj.Name)
}
// ok to continue
}
// typecheck method signatures
for _, m := range scope.Objects {
mdecl := m.Decl.(*ast.FuncDecl)
// TODO(gri) At the moment, the receiver is type-checked when checking
// the method body. Also, we don't properly track if the receiver is
// a pointer (i.e., currently, method sets are too large). FIX THIS.
mtyp := check.typ(mdecl.Type, cycleOk).(*Signature)
m.Type = mtyp
}
}
case ast.Fun: case ast.Fun:
fdecl := obj.Decl.(*ast.FuncDecl) fdecl := obj.Decl.(*ast.FuncDecl)
ftyp := c.makeType(fdecl.Type, ref).(*Func)
obj.Type = ftyp
if fdecl.Recv != nil { if fdecl.Recv != nil {
recvField := fdecl.Recv.List[0] // This will ensure that the method base type is
if len(recvField.Names) > 0 { // type-checked
ftyp.Recv = recvField.Names[0].Obj check.collectFields(token.FUNC, fdecl.Recv, true)
} else {
ftyp.Recv = ast.NewObj(ast.Var, "_")
ftyp.Recv.Decl = recvField
}
c.checkObj(ftyp.Recv, ref)
// TODO(axw) add method to a list in the receiver type.
} }
// TODO(axw) check function body, if non-nil. ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
obj.Type = ftyp
check.function(ftyp, fdecl.Body)
default: default:
panic("unreachable") panic("unreachable")
} }
} }
// Check typechecks a package. // assocInitvals associates "inherited" initialization expressions
// It augments the AST by assigning types to all ast.Objects and returns a map // with the corresponding *ast.ValueSpec in the check.initexprs map
// of types for all expression nodes in statements, and a scanner.ErrorList if // for constant declarations without explicit initialization expressions.
// there are errors.
// //
func Check(fset *token.FileSet, pkg *ast.Package) (types map[ast.Expr]Type, err error) { func (check *checker) assocInitvals(decl *ast.GenDecl) {
// Sort objects so that we get reproducible error var values []ast.Expr
// positions (this is only needed for testing). for _, s := range decl.Specs {
// TODO(gri): Consider ast.Scope implementation that if s, ok := s.(*ast.ValueSpec); ok {
// provides both a list and a map for fast lookup. if len(s.Values) > 0 {
// Would permit the use of scopes instead of ObjMaps values = s.Values
// elsewhere. } else {
list := make(ObjList, len(pkg.Scope.Objects)) check.initexprs[s] = values
i := 0 }
for _, obj := range pkg.Scope.Objects { }
list[i] = obj
i++
} }
list.Sort() if len(values) == 0 {
check.invalidAST(decl.Pos(), "no initialization values provided")
var c checker
c.fset = fset
c.types = make(map[ast.Expr]Type)
for _, obj := range list {
c.checkObj(obj, false)
} }
}
c.errors.RemoveMultiples()
return c.types, c.errors.Err() // assocMethod associates a method declaration with the respective
// receiver base type. meth.Recv must exist.
//
func (check *checker) assocMethod(meth *ast.FuncDecl) {
// The receiver type is one of the following (enforced by parser):
// - *ast.Ident
// - *ast.StarExpr{*ast.Ident}
// - *ast.BadExpr (parser error)
typ := meth.Recv.List[0].Type
if ptr, ok := typ.(*ast.StarExpr); ok {
typ = ptr.X
}
// determine receiver base type object (or nil if error)
var obj *ast.Object
if ident, ok := typ.(*ast.Ident); ok && ident.Obj != nil {
obj = ident.Obj
if obj.Kind != ast.Typ {
check.errorf(ident.Pos(), "%s is not a type", ident.Name)
obj = nil
}
// TODO(gri) determine if obj was defined in this package
/*
if check.notLocal(obj) {
check.errorf(ident.Pos(), "cannot define methods on non-local type %s", ident.Name)
obj = nil
}
*/
} else {
// If it's not an identifier or the identifier wasn't declared/resolved,
// the parser/resolver already reported an error. Nothing to do here.
}
// determine base type scope (or nil if error)
var scope *ast.Scope
if obj != nil {
if obj.Data != nil {
scope = obj.Data.(*ast.Scope)
} else {
scope = ast.NewScope(nil)
obj.Data = scope
}
} else {
// use a dummy scope so that meth can be declared in
// presence of an error and get an associated object
// (always use a new scope so that we don't get double
// declaration errors)
scope = ast.NewScope(nil)
}
check.declare(scope, ast.Fun, meth.Name, meth)
}
func (check *checker) assocInitvalsOrMethod(decl ast.Decl) {
switch d := decl.(type) {
case *ast.GenDecl:
if d.Tok == token.CONST {
check.assocInitvals(d)
}
case *ast.FuncDecl:
if d.Recv != nil {
check.assocMethod(d)
}
}
}
func (check *checker) decl(decl ast.Decl) {
switch d := decl.(type) {
case *ast.BadDecl:
// ignore
case *ast.GenDecl:
for _, spec := range d.Specs {
switch s := spec.(type) {
case *ast.ImportSpec:
// nothing to do (handled by ast.NewPackage)
case *ast.ValueSpec:
for _, name := range s.Names {
if obj := name.Obj; obj.Type == nil {
check.object(obj, false)
}
}
case *ast.TypeSpec:
if obj := s.Name.Obj; obj.Type == nil {
check.object(obj, false)
}
default:
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
}
}
case *ast.FuncDecl:
if d.Name.Name == "init" {
// initialization function
// TODO(gri) ignore for now (has no object associated with it)
// (should probably collect in a first phase and properly initialize)
return
}
if obj := d.Name.Obj; obj.Type == nil {
check.object(obj, false)
}
default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
}
}
// iterate calls f for each package-level declaration.
func (check *checker) iterate(f func(*checker, ast.Decl)) {
list := check.filenames
if list == nil {
// initialize lazily
for filename := range check.pkg.Files {
list = append(list, filename)
}
sort.Strings(list)
check.filenames = list
}
for _, filename := range list {
for _, decl := range check.pkg.Files[filename].Decls {
f(check, decl)
}
}
}
// A bailout panic is raised to indicate early termination.
type bailout struct{}
func check(fset *token.FileSet, pkg *ast.Package, errh func(token.Pos, string), f func(ast.Expr, Type)) (err error) {
// initialize checker
var check checker
check.fset = fset
check.pkg = pkg
check.errh = errh
check.mapf = f
check.initexprs = make(map[*ast.ValueSpec][]ast.Expr)
// handle bailouts
defer func() {
if p := recover(); p != nil {
_ = p.(bailout) // re-panic if not a bailout
}
err = check.firsterr
}()
// determine missing constant initialization expressions
// and associate methods with types
check.iterate((*checker).assocInitvalsOrMethod)
// typecheck all declarations
check.iterate((*checker).decl)
return
} }

Some files were not shown because too many files have changed in this diff Show More