Update Go library to last weekly.

From-SVN: r180552
This commit is contained in:
Ian Lance Taylor 2011-10-26 23:57:58 +00:00
parent e0c39d66d4
commit d8f412571f
537 changed files with 43765 additions and 12999 deletions

View file

@ -13,7 +13,6 @@ package main
import (
"container/heap"
"container/ring"
"container/vector"
)
// Return a chan of odd numbers, starting from 5.
@ -47,13 +46,28 @@ type PeekCh struct {
ch chan int
}
// Heap of PeekCh, sorting by head values.
type PeekChHeap struct {
*vector.Vector
}
// Heap of PeekCh, sorting by head values, satisfies Heap interface.
type PeekChHeap []*PeekCh
func (h *PeekChHeap) Less(i, j int) bool {
return h.At(i).(*PeekCh).head < h.At(j).(*PeekCh).head
return (*h)[i].head < (*h)[j].head
}
func (h *PeekChHeap) Swap(i, j int) {
(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
}
func (h *PeekChHeap) Len() int {
return len(*h)
}
func (h *PeekChHeap) Pop() (v interface{}) {
*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
return
}
func (h *PeekChHeap) Push(v interface{}) {
*h = append(*h, v.(*PeekCh))
}
// Return a channel to serve as a sending proxy to 'out'.
@ -108,26 +122,26 @@ func Sieve() chan int {
// Merge channels of multiples of 'primes' into 'composites'.
go func() {
h := &PeekChHeap{new(vector.Vector)}
var h PeekChHeap
min := 15
for {
m := multiples(<-primes)
head := <-m
for min < head {
composites <- min
minchan := heap.Pop(h).(*PeekCh)
minchan := heap.Pop(&h).(*PeekCh)
min = minchan.head
minchan.head = <-minchan.ch
heap.Push(h, minchan)
heap.Push(&h, minchan)
}
for min == head {
minchan := heap.Pop(h).(*PeekCh)
minchan := heap.Pop(&h).(*PeekCh)
min = minchan.head
minchan.head = <-minchan.ch
heap.Push(h, minchan)
heap.Push(&h, minchan)
}
composites <- head
heap.Push(h, &PeekCh{<-m, m})
heap.Push(&h, &PeekCh{<-m, m})
}
}()

View file

@ -1,64 +0,0 @@
// $G $F.go && $L $F.$A && ./$A.out
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import "container/vector"
type S struct {
val int
}
func (p *S) Init(val int) *S {
p.val = val
return p
}
func test0() {
v := new(vector.Vector)
if v.Len() != 0 {
print("len = ", v.Len(), "\n")
panic("fail")
}
}
func test1() {
var a [1000]*S
for i := 0; i < len(a); i++ {
a[i] = new(S).Init(i)
}
v := new(vector.Vector)
for i := 0; i < len(a); i++ {
v.Insert(0, a[i])
if v.Len() != i+1 {
print("len = ", v.Len(), "\n")
panic("fail")
}
}
for i := 0; i < v.Len(); i++ {
x := v.At(i).(*S)
if x.val != v.Len()-i-1 {
print("expected ", i, ", found ", x.val, "\n")
panic("fail")
}
}
for v.Len() > 10 {
v.Delete(10)
}
}
func main() {
test0()
test1()
}

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -15,36 +15,37 @@ const (
blockSize = 512
// Types
TypeReg = '0'
TypeRegA = '\x00'
TypeLink = '1'
TypeSymlink = '2'
TypeChar = '3'
TypeBlock = '4'
TypeDir = '5'
TypeFifo = '6'
TypeCont = '7'
TypeXHeader = 'x'
TypeXGlobalHeader = 'g'
TypeReg = '0' // regular file.
TypeRegA = '\x00' // regular file.
TypeLink = '1' // hard link.
TypeSymlink = '2' // symbolic link.
TypeChar = '3' // character device node.
TypeBlock = '4' // block device node.
TypeDir = '5' // directory.
TypeFifo = '6' // fifo node.
TypeCont = '7' // reserved.
TypeXHeader = 'x' // extended header.
TypeXGlobalHeader = 'g' // global extended header.
)
// A Header represents a single header in a tar archive.
// Some fields may not be populated.
type Header struct {
Name string
Mode int64
Uid int
Gid int
Size int64
Mtime int64
Typeflag byte
Linkname string
Uname string
Gname string
Devmajor int64
Devminor int64
Atime int64
Ctime int64
Name string // name of header file entry.
Mode int64 // permission and mode bits.
Uid int // user id of owner.
Gid int // group id of owner.
Size int64 // length in bytes.
Mtime int64 // modified time; seconds since epoch.
Typeflag byte // type of header entry.
Linkname string // target name of link.
Uname string // user name of owner.
Gname string // group name of owner.
Devmajor int64 // major number of character or block device.
Devminor int64 // minor number of character or block device.
Atime int64 // access time; seconds since epoch.
Ctime int64 // status change time; seconds since epoch.
}
var zeroBlock = make([]byte, blockSize)

View file

@ -94,7 +94,7 @@ func (tr *Reader) skipUnread() {
return
}
}
_, tr.err = io.Copyn(ioutil.Discard, tr.r, nr)
_, tr.err = io.CopyN(ioutil.Discard, tr.r, nr)
}
func (tr *Reader) verifyChecksum(header []byte) bool {

Binary file not shown.

View file

@ -134,7 +134,7 @@ func (tw *Writer) WriteHeader(hdr *Header) os.Error {
tw.numeric(s.next(12), hdr.Mtime) // 136:148
s.next(8) // chksum (148:156)
s.next(1)[0] = hdr.Typeflag // 156:157
s.next(100) // linkname (157:257)
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

View file

@ -24,6 +24,10 @@ type writerTest struct {
}
var writerTests = []*writerTest{
// The writer test file was produced with this command:
// tar (GNU tar) 1.26
// ln -s small.txt link.txt
// tar -b 1 --format=ustar -c -f writer.tar small.txt small2.txt link.txt
&writerTest{
file: "testdata/writer.tar",
entries: []*writerTestEntry{
@ -55,6 +59,21 @@ var writerTests = []*writerTest{
},
contents: "Google.com\n",
},
&writerTestEntry{
header: &Header{
Name: "link.txt",
Mode: 0777,
Uid: 1000,
Gid: 1000,
Size: 0,
Mtime: 1314603082,
Typeflag: '2',
Linkname: "small.txt",
Uname: "strings",
Gname: "strings",
},
// no contents
},
},
},
// The truncated test file was produced using these commands:

View file

@ -238,7 +238,7 @@ func readDirectoryHeader(f *File, r io.Reader) os.Error {
commentLen := int(c.Uint16(b[32:34]))
// startDiskNumber := c.Uint16(b[34:36]) // Unused
// internalAttributes := c.Uint16(b[36:38]) // Unused
// externalAttributes := c.Uint32(b[38:42]) // Unused
f.ExternalAttrs = c.Uint32(b[38:42])
f.headerOffset = int64(c.Uint32(b[42:46]))
d := make([]byte, filenameLen+extraLen+commentLen)
if _, err := io.ReadFull(r, d); err != nil {

View file

@ -26,6 +26,7 @@ type ZipTestFile struct {
Content []byte // if blank, will attempt to compare against File
File string // name of file to compare to (relative to testdata/)
Mtime string // modified time in format "mm-dd-yy hh:mm:ss"
Mode uint32
}
// Caution: The Mtime values found for the test files should correspond to
@ -47,11 +48,13 @@ var tests = []ZipTest{
Name: "test.txt",
Content: []byte("This is a test text file.\n"),
Mtime: "09-05-10 12:12:02",
Mode: 0x81a4,
},
{
Name: "gophercolor16x16.png",
File: "gophercolor16x16.png",
Mtime: "09-05-10 15:52:58",
Mode: 0x81a4,
},
},
},
@ -162,6 +165,8 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
t.Errorf("%s: mtime=%s (%d); want %s (%d)", f.Name, time.SecondsToUTC(got), got, mtime, want)
}
testFileMode(t, f, ft.Mode)
size0 := f.UncompressedSize
var b bytes.Buffer
@ -203,6 +208,19 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {
}
}
func testFileMode(t *testing.T, f *File, want uint32) {
mode, err := f.Mode()
if want == 0 {
if err == nil {
t.Errorf("%s mode: got %v, want none", f.Name, mode)
}
} else if err != nil {
t.Errorf("%s mode: %s", f.Name, err)
} else if mode != want {
t.Errorf("%s mode: want 0x%x, got 0x%x", f.Name, want, mode)
}
}
func TestInvalidFiles(t *testing.T) {
const size = 1024 * 70 // 70kb
b := make([]byte, size)

View file

@ -28,6 +28,9 @@ const (
directoryHeaderLen = 46 // + filename + extra + comment
directoryEndLen = 22 // + comment
dataDescriptorLen = 12
// Constants for the first byte in CreatorVersion
creatorUnix = 3
)
type FileHeader struct {
@ -42,6 +45,7 @@ type FileHeader struct {
CompressedSize uint32
UncompressedSize uint32
Extra []byte
ExternalAttrs uint32 // Meaning depends on CreatorVersion
Comment string
}
@ -89,3 +93,18 @@ func (h *FileHeader) Mtime_ns() int64 {
t := msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
return t.Seconds() * 1e9
}
// Mode returns the permission and mode bits for the FileHeader.
// An error is returned in case the information is not available.
func (h *FileHeader) Mode() (mode uint32, err os.Error) {
if h.CreatorVersion>>8 == creatorUnix {
return h.ExternalAttrs >> 16, nil
}
return 0, os.NewError("file mode not available")
}
// SetMode changes the permission and mode bits for the FileHeader.
func (h *FileHeader) SetMode(mode uint32) {
h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8
h.ExternalAttrs = mode << 16
}

View file

@ -69,7 +69,7 @@ func (w *Writer) Close() (err os.Error) {
write(w, uint16(len(h.Comment)))
write(w, uint16(0)) // disk number start
write(w, uint16(0)) // internal file attributes
write(w, uint32(0)) // external file attributes
write(w, h.ExternalAttrs)
write(w, h.offset)
writeBytes(w, []byte(h.Name))
writeBytes(w, h.Extra)
@ -115,7 +115,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, os.Error) {
}
fh.Flags |= 0x8 // we will write a data descriptor
fh.CreatorVersion = 0x14
fh.CreatorVersion = fh.CreatorVersion&0xff00 | 0x14
fh.ReaderVersion = 0x14
fw := &fileWriter{

View file

@ -13,19 +13,45 @@ import (
// TODO(adg): a more sophisticated test suite
const testString = "Rabbits, guinea pigs, gophers, marsupial rats, and quolls."
type WriteTest struct {
Name string
Data []byte
Method uint16
Mode uint32
}
var writeTests = []WriteTest{
WriteTest{
Name: "foo",
Data: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),
Method: Store,
},
WriteTest{
Name: "bar",
Data: nil, // large data set in the test
Method: Deflate,
Mode: 0x81ed,
},
}
func TestWriter(t *testing.T) {
largeData := make([]byte, 1<<17)
for i := range largeData {
largeData[i] = byte(rand.Int())
}
writeTests[1].Data = largeData
defer func() {
writeTests[1].Data = nil
}()
// write a zip file
buf := new(bytes.Buffer)
w := NewWriter(buf)
testCreate(t, w, "foo", []byte(testString), Store)
testCreate(t, w, "bar", largeData, Deflate)
for _, wt := range writeTests {
testCreate(t, w, &wt)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
@ -35,26 +61,34 @@ func TestWriter(t *testing.T) {
if err != nil {
t.Fatal(err)
}
testReadFile(t, r.File[0], []byte(testString))
testReadFile(t, r.File[1], largeData)
for i, wt := range writeTests {
testReadFile(t, r.File[i], &wt)
}
}
func testCreate(t *testing.T, w *Writer, name string, data []byte, method uint16) {
func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
header := &FileHeader{
Name: name,
Method: method,
Name: wt.Name,
Method: wt.Method,
}
if wt.Mode != 0 {
header.SetMode(wt.Mode)
}
f, err := w.CreateHeader(header)
if err != nil {
t.Fatal(err)
}
_, err = f.Write(data)
_, err = f.Write(wt.Data)
if err != nil {
t.Fatal(err)
}
}
func testReadFile(t *testing.T, f *File, data []byte) {
func testReadFile(t *testing.T, f *File, wt *WriteTest) {
if f.Name != wt.Name {
t.Fatalf("File name: got %q, want %q", f.Name, wt.Name)
}
testFileMode(t, f, wt.Mode)
rc, err := f.Open()
if err != nil {
t.Fatal("opening:", err)
@ -67,7 +101,7 @@ func testReadFile(t *testing.T, f *File, data []byte) {
if err != nil {
t.Fatal("closing:", err)
}
if !bytes.Equal(b, data) {
t.Errorf("File contents %q, want %q", b, data)
if !bytes.Equal(b, wt.Data) {
t.Errorf("File contents %q, want %q", b, wt.Data)
}
}

View file

@ -516,6 +516,8 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
result, err = parseIA5String(innerBytes)
case tagT61String:
result, err = parseT61String(innerBytes)
case tagUTF8String:
result, err = parseUTF8String(innerBytes)
case tagInteger:
result, err = parseInt64(innerBytes)
case tagBitString:

View file

@ -206,10 +206,10 @@ type timeTest struct {
}
var utcTestData = []timeTest{
{"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, -7 * 60 * 60, ""}},
{"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 0, 7*60*60 + 30*60, ""}},
{"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, 0, "UTC"}},
{"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, 0, "UTC"}},
{"910506164540-0700", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, -7 * 60 * 60, ""}},
{"910506164540+0730", true, &time.Time{1991, 05, 06, 16, 45, 40, 0, 7*60*60 + 30*60, ""}},
{"910506234540Z", true, &time.Time{1991, 05, 06, 23, 45, 40, 0, 0, "UTC"}},
{"9105062345Z", true, &time.Time{1991, 05, 06, 23, 45, 0, 0, 0, "UTC"}},
{"a10506234540Z", false, nil},
{"91a506234540Z", false, nil},
{"9105a6234540Z", false, nil},
@ -235,10 +235,10 @@ func TestUTCTime(t *testing.T) {
}
var generalizedTimeTestData = []timeTest{
{"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 0, "UTC"}},
{"20100102030405Z", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, "UTC"}},
{"20100102030405", false, nil},
{"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, 6*60*60 + 7*60, ""}},
{"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 0, -6*60*60 - 7*60, ""}},
{"20100102030405+0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, 6*60*60 + 7*60, ""}},
{"20100102030405-0607", true, &time.Time{2010, 01, 02, 03, 04, 05, 0, -6*60*60 - 7*60, ""}},
}
func TestGeneralizedTime(t *testing.T) {
@ -475,7 +475,7 @@ var derEncodedSelfSignedCert = Certificate{
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 3}, Value: "false.example.com"}},
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, Value: "false@example.com"}},
},
Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}},
Validity: Validity{NotBefore: &time.Time{Year: 2009, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}, NotAfter: &time.Time{Year: 2010, Month: 10, Day: 8, Hour: 0, Minute: 25, Second: 53, ZoneOffset: 0, Zone: "UTC"}},
Subject: RDNSequence{
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 6}, Value: "XX"}},
RelativeDistinguishedNameSET{AttributeTypeAndValue{Type: ObjectIdentifier{2, 5, 4, 8}, Value: "Some-State"}},

View file

@ -464,11 +464,15 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
if v.Type() == rawValueType {
rv := v.Interface().(RawValue)
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
if err != nil {
return
if len(rv.FullBytes) != 0 {
_, err = out.Write(rv.FullBytes)
} else {
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
if err != nil {
return
}
_, err = out.Write(rv.Bytes)
}
_, err = out.Write(rv.Bytes)
return
}

View file

@ -163,7 +163,7 @@ func (z *Int) Binomial(n, k int64) *Int {
// Quo sets z to the quotient x/y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// See QuoRem for more details.
// Quo implements truncated division (like Go); see QuoRem for more details.
func (z *Int) Quo(x, y *Int) *Int {
z.abs, _ = z.abs.div(nil, x.abs, y.abs)
z.neg = len(z.abs) > 0 && x.neg != y.neg // 0 has no sign
@ -172,7 +172,7 @@ func (z *Int) Quo(x, y *Int) *Int {
// Rem sets z to the remainder x%y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// See QuoRem for more details.
// Rem implements truncated modulus (like Go); see QuoRem for more details.
func (z *Int) Rem(x, y *Int) *Int {
_, z.abs = nat(nil).div(z.abs, x.abs, y.abs)
z.neg = len(z.abs) > 0 && x.neg // 0 has no sign
@ -198,7 +198,7 @@ func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) {
// Div sets z to the quotient x/y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// See DivMod for more details.
// Div implements Euclidean division (unlike Go); see DivMod for more details.
func (z *Int) Div(x, y *Int) *Int {
y_neg := y.neg // z may be an alias for y
var r Int
@ -215,7 +215,7 @@ func (z *Int) Div(x, y *Int) *Int {
// Mod sets z to the modulus x%y for y != 0 and returns z.
// If y == 0, a division-by-zero run-time panic occurs.
// See DivMod for more details.
// Mod implements Euclidean modulus (unlike Go); see DivMod for more details.
func (z *Int) Mod(x, y *Int) *Int {
y0 := y // save y
if z == y || alias(z.abs, y.abs) {

View file

@ -301,6 +301,9 @@ func TestGetString(t *testing.T) {
func TestSetString(t *testing.T) {
tmp := new(Int)
for i, test := range stringTests {
// initialize to a non-zero value so that issues with parsing
// 0 are detected
tmp.SetInt64(1234567890)
n1, ok1 := new(Int).SetString(test.in, test.base)
n2, ok2 := tmp.SetString(test.in, test.base)
expected := NewInt(test.val)

View file

@ -646,7 +646,7 @@ func (z nat) scan(r io.RuneScanner, base int) (nat, int, os.Error) {
}
}
case os.EOF:
return z, 10, nil
return z.make(0), 10, nil
default:
return z, 10, err
}

View file

@ -27,9 +27,13 @@ func NewRat(a, b int64) *Rat {
// SetFrac sets z to a/b and returns z.
func (z *Rat) SetFrac(a, b *Int) *Rat {
z.a.Set(a)
z.a.neg = a.neg != b.neg
z.b = z.b.set(b.abs)
babs := b.abs
if &z.a == b || alias(z.a.abs, babs) {
babs = nat(nil).set(babs) // make a copy
}
z.a.abs = z.a.abs.set(a.abs)
z.b = z.b.set(babs)
return z.norm()
}

View file

@ -330,3 +330,43 @@ func TestRatGobEncoding(t *testing.T) {
}
}
}
func TestIssue2379(t *testing.T) {
// 1) no aliasing
q := NewRat(3, 2)
x := new(Rat)
x.SetFrac(NewInt(3), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("1) got %s want %s", x, q)
}
// 2) aliasing of numerator
x = NewRat(2, 3)
x.SetFrac(NewInt(3), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("2) got %s want %s", x, q)
}
// 3) aliasing of denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), NewInt(2))
if x.Cmp(q) != 0 {
t.Errorf("3) got %s want %s", x, q)
}
// 4) aliasing of numerator and denominator
x = NewRat(2, 3)
x.SetFrac(x.Denom(), x.Num())
if x.Cmp(q) != 0 {
t.Errorf("4) got %s want %s", x, q)
}
// 5) numerator and denominator are the same
q = NewRat(1, 1)
x = new(Rat)
n := NewInt(7)
x.SetFrac(n, n)
if x.Cmp(q) != 0 {
t.Errorf("5) got %s want %s", x, q)
}
}

View file

@ -54,11 +54,11 @@ type Reader struct {
}
// NewReaderSize creates a new Reader whose buffer has the specified size,
// which must be greater than zero. If the argument io.Reader is already a
// which must be greater than one. If the argument io.Reader is already a
// Reader with large enough size, it returns the underlying Reader.
// It returns the Reader and any error.
func NewReaderSize(rd io.Reader, size int) (*Reader, os.Error) {
if size <= 0 {
if size <= 1 {
return nil, BufSizeError(size)
}
// Is it already a Reader?
@ -298,6 +298,17 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err os.Error) {
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
line, err = b.ReadSlice('\n')
if err == ErrBufferFull {
// Handle the case where "\r\n" straddles the buffer.
if len(line) > 0 && line[len(line)-1] == '\r' {
// Put the '\r' back on buf and drop it from line.
// Let the next call to ReadLine check for "\r\n".
if b.r == 0 {
// should be unreachable
panic("bufio: tried to rewind past start of buffer")
}
b.r--
line = line[:len(line)-1]
}
return line, true, nil
}
@ -307,10 +318,11 @@ func (b *Reader) ReadLine() (line []byte, isPrefix bool, err os.Error) {
err = nil
if line[len(line)-1] == '\n' {
line = line[:len(line)-1]
}
if len(line) > 0 && line[len(line)-1] == '\r' {
line = line[:len(line)-1]
drop := 1
if len(line) > 1 && line[len(line)-2] == '\r' {
drop = 2
}
line = line[:len(line)-drop]
}
return
}

View file

@ -137,7 +137,7 @@ var bufreaders = []bufReader{
}
var bufsizes = []int{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
2, 3, 4, 5, 6, 7, 8, 9, 10,
23, 32, 46, 64, 93, 128, 1024, 4096,
}
@ -697,3 +697,71 @@ func TestLinesAfterRead(t *testing.T) {
t.Errorf("expected EOF from ReadLine, got '%s' %t %s", line, isPrefix, err)
}
}
type readLineResult struct {
line []byte
isPrefix bool
err os.Error
}
var readLineNewlinesTests = []struct {
input string
bufSize int
expect []readLineResult
}{
{"h\r\nb\r\n", 2, []readLineResult{
{[]byte("h"), true, nil},
{nil, false, nil},
{[]byte("b"), true, nil},
{nil, false, nil},
{nil, false, os.EOF},
}},
{"hello\r\nworld\r\n", 6, []readLineResult{
{[]byte("hello"), true, nil},
{nil, false, nil},
{[]byte("world"), true, nil},
{nil, false, nil},
{nil, false, os.EOF},
}},
{"hello\rworld\r", 6, []readLineResult{
{[]byte("hello"), true, nil},
{[]byte("\rworld"), true, nil},
{[]byte("\r"), false, nil},
{nil, false, os.EOF},
}},
{"h\ri\r\n\r", 2, []readLineResult{
{[]byte("h"), true, nil},
{[]byte("\ri"), true, nil},
{nil, false, nil},
{[]byte("\r"), false, nil},
{nil, false, os.EOF},
}},
}
func TestReadLineNewlines(t *testing.T) {
for _, e := range readLineNewlinesTests {
testReadLineNewlines(t, e.input, e.bufSize, e.expect)
}
}
func testReadLineNewlines(t *testing.T, input string, bufSize int, expect []readLineResult) {
b, err := NewReaderSize(strings.NewReader(input), bufSize)
if err != nil {
t.Fatal(err)
}
for i, e := range expect {
line, isPrefix, err := b.ReadLine()
if bytes.Compare(line, e.line) != 0 {
t.Errorf("%q call %d, line == %q, want %q", input, i, line, e.line)
return
}
if isPrefix != e.isPrefix {
t.Errorf("%q call %d, isPrefix == %v, want %v", input, i, isPrefix, e.isPrefix)
return
}
if err != e.err {
t.Errorf("%q call %d, err == %v, want %v", input, i, err, e.err)
return
}
}
}

View file

@ -336,13 +336,18 @@ func (b *Buffer) ReadString(delim byte) (line string, err os.Error) {
// NewBuffer creates and initializes a new Buffer using buf as its initial
// contents. It is intended to prepare a Buffer to read existing data. It
// can also be used to size the internal buffer for writing. To do that,
// can also be used to size the internal buffer for writing. To do that,
// buf should have the desired capacity but a length of zero.
//
// In most cases, new(Buffer) (or just declaring a Buffer variable) is
// preferable to NewBuffer. In particular, passing a non-empty buf to
// NewBuffer and then writing to the Buffer will overwrite buf, not append to
// it.
func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }
// NewBufferString creates and initializes a new Buffer using string s as its
// initial contents. It is intended to prepare a buffer to read an existing
// string.
// string. See the warnings about NewBuffer; similar issues apply here.
func NewBufferString(s string) *Buffer {
return &Buffer{buf: []byte(s)}
}

View file

@ -572,13 +572,18 @@ func Runes(s []byte) []int {
// non-overlapping instances of old replaced by new.
// If n < 0, there is no limit on the number of replacements.
func Replace(s, old, new []byte, n int) []byte {
if n == 0 {
return s // avoid allocation
m := 0
if n != 0 {
// Compute number of replacements.
m = Count(s, old)
}
// Compute number of replacements.
if m := Count(s, old); m == 0 {
return s // avoid allocation
} else if n <= 0 || m < n {
if m == 0 {
// Nothing to do. Just copy.
t := make([]byte, len(s))
copy(t, s)
return t
}
if n < 0 || m < n {
n = m
}
@ -603,3 +608,58 @@ func Replace(s, old, new []byte, n int) []byte {
w += copy(t[w:], s[start:])
return t[0:w]
}
// EqualFold reports whether s and t, interpreted as UTF-8 strings,
// are equal under Unicode case-folding.
func EqualFold(s, t []byte) bool {
for len(s) != 0 && len(t) != 0 {
// Extract first rune from each.
var sr, tr int
if s[0] < utf8.RuneSelf {
sr, s = int(s[0]), s[1:]
} else {
r, size := utf8.DecodeRune(s)
sr, s = r, s[size:]
}
if t[0] < utf8.RuneSelf {
tr, t = int(t[0]), t[1:]
} else {
r, size := utf8.DecodeRune(t)
tr, t = r, t[size:]
}
// If they match, keep going; if not, return false.
// Easy case.
if tr == sr {
continue
}
// Make sr < tr to simplify what follows.
if tr < sr {
tr, sr = sr, tr
}
// Fast check for ASCII.
if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' {
// ASCII, and sr is upper case. tr must be lower case.
if tr == sr+'a'-'A' {
continue
}
return false
}
// General case. SimpleFold(x) returns the next equivalent rune > x
// or wraps around to smaller values.
r := unicode.SimpleFold(sr)
for r != sr && r < tr {
r = unicode.SimpleFold(r)
}
if r == tr {
continue
}
return false
}
// One string is empty. Are both?
return len(s) == len(t)
}

View file

@ -829,9 +829,15 @@ var ReplaceTests = []ReplaceTest{
func TestReplace(t *testing.T) {
for _, tt := range ReplaceTests {
if s := string(Replace([]byte(tt.in), []byte(tt.old), []byte(tt.new), tt.n)); s != tt.out {
in := append([]byte(tt.in), "<spare>"...)
in = in[:len(tt.in)]
out := Replace(in, []byte(tt.old), []byte(tt.new), tt.n)
if s := string(out); s != tt.out {
t.Errorf("Replace(%q, %q, %q, %d) = %q, want %q", tt.in, tt.old, tt.new, tt.n, s, tt.out)
}
if cap(in) == cap(out) && &in[:1][0] == &out[:1][0] {
t.Errorf("Replace(%q, %q, %q, %d) didn't copy", tt.in, tt.old, tt.new, tt.n)
}
}
}
@ -856,3 +862,31 @@ func TestTitle(t *testing.T) {
}
}
}
var EqualFoldTests = []struct {
s, t string
out bool
}{
{"abc", "abc", true},
{"ABcd", "ABcd", true},
{"123abc", "123ABC", true},
{"αβδ", "ΑΒΔ", true},
{"abc", "xyz", false},
{"abc", "XYZ", false},
{"abcdefghijk", "abcdefghijX", false},
{"abcdefghijk", "abcdefghij\u212A", true},
{"abcdefghijK", "abcdefghij\u212A", true},
{"abcdefghijkz", "abcdefghij\u212Ay", false},
{"abcdefghijKz", "abcdefghij\u212Ay", false},
}
func TestEqualFold(t *testing.T) {
for _, tt := range EqualFoldTests {
if out := EqualFold([]byte(tt.s), []byte(tt.t)); out != tt.out {
t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.s, tt.t, out, tt.out)
}
if out := EqualFold([]byte(tt.t), []byte(tt.s)); out != tt.out {
t.Errorf("EqualFold(%#q, %#q) = %v, want %v", tt.t, tt.s, out, tt.out)
}
}
}

View file

@ -50,7 +50,7 @@ import "math"
// Asin returns the inverse sine of x.
func Asin(x complex128) complex128 {
if imag(x) == 0 {
if math.Fabs(real(x)) > 1 {
if math.Abs(real(x)) > 1 {
return complex(math.Pi/2, 0) // DOMAIN error
}
return complex(math.Asin(real(x)), 0)
@ -67,7 +67,7 @@ func Asin(x complex128) complex128 {
func Asinh(x complex128) complex128 {
// TODO check range
if imag(x) == 0 {
if math.Fabs(real(x)) > 1 {
if math.Abs(real(x)) > 1 {
return complex(math.Pi/2, 0) // DOMAIN error
}
return complex(math.Asinh(real(x)), 0)

View file

@ -122,7 +122,7 @@ func Cosh(x complex128) complex128 {
// calculate sinh and cosh
func sinhcosh(x float64) (sh, ch float64) {
if math.Fabs(x) <= 0.5 {
if math.Abs(x) <= 0.5 {
return math.Sinh(x), math.Cosh(x)
}
e := math.Exp(x)

View file

@ -76,7 +76,7 @@ func Sqrt(x complex128) complex128 {
b := imag(x)
var scale float64
// Rescale to avoid internal overflow or underflow.
if math.Fabs(a) > 4 || math.Fabs(b) > 4 {
if math.Abs(a) > 4 || math.Abs(b) > 4 {
a *= 0.25
b *= 0.25
scale = 2
@ -89,11 +89,11 @@ func Sqrt(x complex128) complex128 {
var t float64
if a > 0 {
t = math.Sqrt(0.5*r + 0.5*a)
r = scale * math.Fabs((0.5*b)/t)
r = scale * math.Abs((0.5*b)/t)
t *= scale
} else {
r = math.Sqrt(0.5*r - 0.5*a)
t = scale * math.Fabs((0.5*b)/r)
t = scale * math.Abs((0.5*b)/r)
r *= scale
}
if b < 0 {

View file

@ -58,7 +58,7 @@ import "math"
// Tan returns the tangent of x.
func Tan(x complex128) complex128 {
d := math.Cos(2*real(x)) + math.Cosh(2*imag(x))
if math.Fabs(d) < 0.25 {
if math.Abs(d) < 0.25 {
d = tanSeries(x)
}
if d == 0 {
@ -109,8 +109,8 @@ func reducePi(x float64) float64 {
// Taylor series expansion for cosh(2y) - cos(2x)
func tanSeries(z complex128) float64 {
const MACHEP = 1.0 / (1 << 53)
x := math.Fabs(2 * real(z))
y := math.Fabs(2 * imag(z))
x := math.Abs(2 * real(z))
y := math.Abs(2 * imag(z))
x = reducePi(x)
x = x * x
y = y * y
@ -139,7 +139,7 @@ func tanSeries(z complex128) float64 {
t = y2 - x2
t /= f
d += t
if math.Fabs(t/d) <= MACHEP {
if math.Abs(t/d) <= MACHEP {
break
}
}
@ -174,7 +174,7 @@ func tanSeries(z complex128) float64 {
// Cot returns the cotangent of x.
func Cot(x complex128) complex128 {
d := math.Cosh(2*imag(x)) - math.Cos(2*real(x))
if math.Fabs(d) < 0.25 {
if math.Abs(d) < 0.25 {
d = tanSeries(x)
}
if d == 0 {

View file

@ -6,32 +6,46 @@ package heap_test
import (
"testing"
"container/vector"
. "container/heap"
)
type myHeap struct {
// A vector.Vector implements sort.Interface except for Less,
// and it implements Push and Pop as required for heap.Interface.
vector.Vector
type myHeap []int
func (h *myHeap) Less(i, j int) bool {
return (*h)[i] < (*h)[j]
}
func (h *myHeap) Less(i, j int) bool { return h.At(i).(int) < h.At(j).(int) }
func (h *myHeap) Swap(i, j int) {
(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
}
func (h *myHeap) verify(t *testing.T, i int) {
func (h *myHeap) Len() int {
return len(*h)
}
func (h *myHeap) Pop() (v interface{}) {
*h, v = (*h)[:h.Len()-1], (*h)[h.Len()-1]
return
}
func (h *myHeap) Push(v interface{}) {
*h = append(*h, v.(int))
}
func (h myHeap) verify(t *testing.T, i int) {
n := h.Len()
j1 := 2*i + 1
j2 := 2*i + 2
if j1 < n {
if h.Less(j1, i) {
t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j1))
t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j1])
return
}
h.verify(t, j1)
}
if j2 < n {
if h.Less(j2, i) {
t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h.At(i), j1, h.At(j2))
t.Errorf("heap invariant invalidated [%d] = %d > [%d] = %d", i, h[i], j1, h[j2])
return
}
h.verify(t, j2)

View file

@ -1,43 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package vector implements containers for managing sequences of elements.
// Vectors grow and shrink dynamically as necessary.
package vector
// Vector is a container for numbered sequences of elements of type interface{}.
// A vector's length and capacity adjusts automatically as necessary.
// The zero value for Vector is an empty vector ready to use.
type Vector []interface{}
// IntVector is a container for numbered sequences of elements of type int.
// A vector's length and capacity adjusts automatically as necessary.
// The zero value for IntVector is an empty vector ready to use.
type IntVector []int
// StringVector is a container for numbered sequences of elements of type string.
// A vector's length and capacity adjusts automatically as necessary.
// The zero value for StringVector is an empty vector ready to use.
type StringVector []string
// Initial underlying array size
const initialSize = 8
// Partial sort.Interface support
// LessInterface provides partial support of the sort.Interface.
type LessInterface interface {
Less(y interface{}) bool
}
// Less returns a boolean denoting whether the i'th element is less than the j'th element.
func (p *Vector) Less(i, j int) bool { return (*p)[i].(LessInterface).Less((*p)[j]) }
// sort.Interface support
// Less returns a boolean denoting whether the i'th element is less than the j'th element.
func (p *IntVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] }
// Less returns a boolean denoting whether the i'th element is less than the j'th element.
func (p *StringVector) Less(i, j int) bool { return (*p)[i] < (*p)[j] }

View file

@ -1,188 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CAUTION: If this file is not vector.go, it was generated
// automatically from vector.go - DO NOT EDIT in that case!
package vector
func (p *IntVector) realloc(length, capacity int) (b []int) {
if capacity < initialSize {
capacity = initialSize
}
if capacity < length {
capacity = length
}
b = make(IntVector, length, capacity)
copy(b, *p)
*p = b
return
}
// Insert n elements at position i.
func (p *IntVector) Expand(i, n int) {
a := *p
// make sure we have enough space
len0 := len(a)
len1 := len0 + n
if len1 <= cap(a) {
// enough space - just expand
a = a[0:len1]
} else {
// not enough space - double capacity
capb := cap(a) * 2
if capb < len1 {
// still not enough - use required length
capb = len1
}
// capb >= len1
a = p.realloc(len1, capb)
}
// make a hole
for j := len0 - 1; j >= i; j-- {
a[j+n] = a[j]
}
*p = a
}
// Insert n elements at the end of a vector.
func (p *IntVector) Extend(n int) { p.Expand(len(*p), n) }
// Resize changes the length and capacity of a vector.
// If the new length is shorter than the current length, Resize discards
// trailing elements. If the new length is longer than the current length,
// Resize adds the respective zero values for the additional elements. The capacity
// parameter is ignored unless the new length or capacity is longer than the current
// capacity. The resized vector's capacity may be larger than the requested capacity.
func (p *IntVector) Resize(length, capacity int) *IntVector {
a := *p
if length > cap(a) || capacity > cap(a) {
// not enough space or larger capacity requested explicitly
a = p.realloc(length, capacity)
} else if length < len(a) {
// clear trailing elements
for i := range a[length:] {
var zero int
a[length+i] = zero
}
}
*p = a[0:length]
return p
}
// Len returns the number of elements in the vector.
// Same as len(*p).
func (p *IntVector) Len() int { return len(*p) }
// Cap returns the capacity of the vector; that is, the
// maximum length the vector can grow without resizing.
// Same as cap(*p).
func (p *IntVector) Cap() int { return cap(*p) }
// At returns the i'th element of the vector.
func (p *IntVector) At(i int) int { return (*p)[i] }
// Set sets the i'th element of the vector to value x.
func (p *IntVector) Set(i int, x int) { (*p)[i] = x }
// Last returns the element in the vector of highest index.
func (p *IntVector) Last() int { return (*p)[len(*p)-1] }
// Copy makes a copy of the vector and returns it.
func (p *IntVector) Copy() IntVector {
arr := make(IntVector, len(*p))
copy(arr, *p)
return arr
}
// Insert inserts into the vector an element of value x before
// the current element at index i.
func (p *IntVector) Insert(i int, x int) {
p.Expand(i, 1)
(*p)[i] = x
}
// Delete deletes the i'th element of the vector. The gap is closed so the old
// element at index i+1 has index i afterwards.
func (p *IntVector) Delete(i int) {
a := *p
n := len(a)
copy(a[i:n-1], a[i+1:n])
var zero int
a[n-1] = zero // support GC, zero out entry
*p = a[0 : n-1]
}
// InsertVector inserts into the vector the contents of the vector
// x such that the 0th element of x appears at index i after insertion.
func (p *IntVector) InsertVector(i int, x *IntVector) {
b := *x
p.Expand(i, len(b))
copy((*p)[i:i+len(b)], b)
}
// Cut deletes elements i through j-1, inclusive.
func (p *IntVector) Cut(i, j int) {
a := *p
n := len(a)
m := n - (j - i)
copy(a[i:m], a[j:n])
for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector.
var zero int
a[k] = zero // support GC, zero out entries
}
*p = a[0:m]
}
// Slice returns a new sub-vector by slicing the old one to extract slice [i:j].
// The elements are copied. The original vector is unchanged.
func (p *IntVector) Slice(i, j int) *IntVector {
var s IntVector
s.realloc(j-i, 0) // will fail in Init() if j < i
copy(s, (*p)[i:j])
return &s
}
// Convenience wrappers
// Push appends x to the end of the vector.
func (p *IntVector) Push(x int) { p.Insert(len(*p), x) }
// Pop deletes the last element of the vector.
func (p *IntVector) Pop() int {
a := *p
i := len(a) - 1
x := a[i]
var zero int
a[i] = zero // support GC, zero out entry
*p = a[0:i]
return x
}
// AppendVector appends the entire vector x to the end of this vector.
func (p *IntVector) AppendVector(x *IntVector) { p.InsertVector(len(*p), x) }
// Swap exchanges the elements at indexes i and j.
func (p *IntVector) Swap(i, j int) {
a := *p
a[i], a[j] = a[j], a[i]
}
// Do calls function f for each element of the vector, in order.
// The behavior of Do is undefined if f changes *p.
func (p *IntVector) Do(f func(elem int)) {
for _, e := range *p {
f(e)
}
}

View file

@ -1,331 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CAUTION: If this file is not vector_test.go, it was generated
// automatically from vector_test.go - DO NOT EDIT in that case!
package vector
import "testing"
func TestIntZeroLen(t *testing.T) {
a := new(IntVector)
if a.Len() != 0 {
t.Errorf("%T: B1) expected 0, got %d", a, a.Len())
}
if len(*a) != 0 {
t.Errorf("%T: B2) expected 0, got %d", a, len(*a))
}
var b IntVector
if b.Len() != 0 {
t.Errorf("%T: B3) expected 0, got %d", b, b.Len())
}
if len(b) != 0 {
t.Errorf("%T: B4) expected 0, got %d", b, len(b))
}
}
func TestIntResize(t *testing.T) {
var a IntVector
checkSize(t, &a, 0, 0)
checkSize(t, a.Resize(0, 5), 0, 5)
checkSize(t, a.Resize(1, 0), 1, 5)
checkSize(t, a.Resize(10, 0), 10, 10)
checkSize(t, a.Resize(5, 0), 5, 10)
checkSize(t, a.Resize(3, 8), 3, 10)
checkSize(t, a.Resize(0, 100), 0, 100)
checkSize(t, a.Resize(11, 100), 11, 100)
}
func TestIntResize2(t *testing.T) {
var a IntVector
checkSize(t, &a, 0, 0)
a.Push(int2IntValue(1))
a.Push(int2IntValue(2))
a.Push(int2IntValue(3))
a.Push(int2IntValue(4))
checkSize(t, &a, 4, 4)
checkSize(t, a.Resize(10, 0), 10, 10)
for i := 4; i < a.Len(); i++ {
if a.At(i) != intzero {
t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, intzero, a.At(i))
}
}
for i := 4; i < len(a); i++ {
if a[i] != intzero {
t.Errorf("%T: expected a[%d] == %v; found %v", a, i, intzero, a[i])
}
}
}
func checkIntZero(t *testing.T, a *IntVector, i int) {
for j := 0; j < i; j++ {
if a.At(j) == intzero {
t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j))
}
if (*a)[j] == intzero {
t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j])
}
}
for ; i < a.Len(); i++ {
if a.At(i) != intzero {
t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, intzero, a.At(i))
}
if (*a)[i] != intzero {
t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, intzero, (*a)[i])
}
}
}
func TestIntTrailingElements(t *testing.T) {
var a IntVector
for i := 0; i < 10; i++ {
a.Push(int2IntValue(i + 1))
}
checkIntZero(t, &a, 10)
checkSize(t, &a, 10, 16)
checkSize(t, a.Resize(5, 0), 5, 16)
checkSize(t, a.Resize(10, 0), 10, 16)
checkIntZero(t, &a, 5)
}
func TestIntAccess(t *testing.T) {
const n = 100
var a IntVector
a.Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2IntValue(val(i)))
}
for i := 0; i < n; i++ {
if elem2IntValue(a.At(i)) != int2IntValue(val(i)) {
t.Error(i)
}
}
var b IntVector
b.Resize(n, 0)
for i := 0; i < n; i++ {
b[i] = int2IntValue(val(i))
}
for i := 0; i < n; i++ {
if elem2IntValue(b[i]) != int2IntValue(val(i)) {
t.Error(i)
}
}
}
func TestIntInsertDeleteClear(t *testing.T) {
const n = 100
var a IntVector
for i := 0; i < n; i++ {
if a.Len() != i {
t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i)
}
a.Insert(0, int2IntValue(val(i)))
if elem2IntValue(a.Last()) != int2IntValue(val(0)) {
t.Errorf("%T: B", a)
}
}
for i := n - 1; i >= 0; i-- {
if elem2IntValue(a.Last()) != int2IntValue(val(0)) {
t.Errorf("%T: C", a)
}
if elem2IntValue(a.At(0)) != int2IntValue(val(i)) {
t.Errorf("%T: D", a)
}
if elem2IntValue(a[0]) != int2IntValue(val(i)) {
t.Errorf("%T: D2", a)
}
a.Delete(0)
if a.Len() != i {
t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i)
}
}
if a.Len() != 0 {
t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a))
}
for i := 0; i < n; i++ {
a.Push(int2IntValue(val(i)))
if a.Len() != i+1 {
t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1)
}
if len(a) != i+1 {
t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1)
}
if elem2IntValue(a.Last()) != int2IntValue(val(i)) {
t.Errorf("%T: H", a)
}
}
a.Resize(0, 0)
if a.Len() != 0 {
t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a))
}
const m = 5
for j := 0; j < m; j++ {
a.Push(int2IntValue(j))
for i := 0; i < n; i++ {
x := val(i)
a.Push(int2IntValue(x))
if elem2IntValue(a.Pop()) != int2IntValue(x) {
t.Errorf("%T: J", a)
}
if a.Len() != j+1 {
t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1)
}
if len(a) != j+1 {
t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1)
}
}
}
if a.Len() != m {
t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m)
}
if len(a) != m {
t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m)
}
}
func verify_sliceInt(t *testing.T, x *IntVector, elt, i, j int) {
for k := i; k < j; k++ {
if elem2IntValue(x.At(k)) != int2IntValue(elt) {
t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt))
}
}
s := x.Slice(i, j)
for k, n := 0, j-i; k < n; k++ {
if elem2IntValue(s.At(k)) != int2IntValue(elt) {
t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2IntValue(x.At(k)), int2IntValue(elt))
}
}
}
func verify_patternInt(t *testing.T, x *IntVector, a, b, c int) {
n := a + b + c
if x.Len() != n {
t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n)
}
if len(*x) != n {
t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n)
}
verify_sliceInt(t, x, 0, 0, a)
verify_sliceInt(t, x, 1, a, a+b)
verify_sliceInt(t, x, 0, a+b, n)
}
func make_vectorInt(elt, len int) *IntVector {
x := new(IntVector).Resize(len, 0)
for i := 0; i < len; i++ {
x.Set(i, int2IntValue(elt))
}
return x
}
func TestIntInsertVector(t *testing.T) {
// 1
a := make_vectorInt(0, 0)
b := make_vectorInt(1, 10)
a.InsertVector(0, b)
verify_patternInt(t, a, 0, 10, 0)
// 2
a = make_vectorInt(0, 10)
b = make_vectorInt(1, 0)
a.InsertVector(5, b)
verify_patternInt(t, a, 5, 0, 5)
// 3
a = make_vectorInt(0, 10)
b = make_vectorInt(1, 3)
a.InsertVector(3, b)
verify_patternInt(t, a, 3, 3, 7)
// 4
a = make_vectorInt(0, 10)
b = make_vectorInt(1, 1000)
a.InsertVector(8, b)
verify_patternInt(t, a, 8, 1000, 2)
}
func TestIntDo(t *testing.T) {
const n = 25
const salt = 17
a := new(IntVector).Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2IntValue(salt*i))
}
count := 0
a.Do(func(e int) {
i := intf2IntValue(e)
if i != int2IntValue(count*salt) {
t.Error(tname(a), "value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(a), "should visit", n, "values; did visit", count)
}
b := new(IntVector).Resize(n, 0)
for i := 0; i < n; i++ {
(*b)[i] = int2IntValue(salt * i)
}
count = 0
b.Do(func(e int) {
i := intf2IntValue(e)
if i != int2IntValue(count*salt) {
t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(b), "b) should visit", n, "values; did visit", count)
}
var c IntVector
c.Resize(n, 0)
for i := 0; i < n; i++ {
c[i] = int2IntValue(salt * i)
}
count = 0
c.Do(func(e int) {
i := intf2IntValue(e)
if i != int2IntValue(count*salt) {
t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(c), "c) should visit", n, "values; did visit", count)
}
}
func TestIntVectorCopy(t *testing.T) {
// verify Copy() returns a copy, not simply a slice of the original vector
const Len = 10
var src IntVector
for i := 0; i < Len; i++ {
src.Push(int2IntValue(i * i))
}
dest := src.Copy()
for i := 0; i < Len; i++ {
src[i] = int2IntValue(-1)
v := elem2IntValue(dest[i])
if v != int2IntValue(i*i) {
t.Error(tname(src), "expected", i*i, "got", v)
}
}
}

View file

@ -1,67 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vector
import (
"fmt"
"sort"
"testing"
)
var (
zero interface{}
intzero int
strzero string
)
func int2Value(x int) int { return x }
func int2IntValue(x int) int { return x }
func int2StrValue(x int) string { return string(x) }
func elem2Value(x interface{}) int { return x.(int) }
func elem2IntValue(x int) int { return x }
func elem2StrValue(x string) string { return x }
func intf2Value(x interface{}) int { return x.(int) }
func intf2IntValue(x interface{}) int { return x.(int) }
func intf2StrValue(x interface{}) string { return x.(string) }
type VectorInterface interface {
Len() int
Cap() int
}
func checkSize(t *testing.T, v VectorInterface, len, cap int) {
if v.Len() != len {
t.Errorf("%T expected len = %d; found %d", v, len, v.Len())
}
if v.Cap() < cap {
t.Errorf("%T expected cap >= %d; found %d", v, cap, v.Cap())
}
}
func val(i int) int { return i*991 - 1234 }
func TestSorting(t *testing.T) {
const n = 100
a := new(IntVector).Resize(n, 0)
for i := n - 1; i >= 0; i-- {
a.Set(i, n-1-i)
}
if sort.IsSorted(a) {
t.Error("int vector not sorted")
}
b := new(StringVector).Resize(n, 0)
for i := n - 1; i >= 0; i-- {
b.Set(i, fmt.Sprint(n-1-i))
}
if sort.IsSorted(b) {
t.Error("string vector not sorted")
}
}
func tname(x interface{}) string { return fmt.Sprintf("%T: ", x) }

View file

@ -1,123 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package vector
import (
"fmt"
"runtime"
"strings"
"testing"
)
const memTestN = 1000000
func s(n uint64) string {
str := fmt.Sprintf("%d", n)
lens := len(str)
a := make([]string, (lens+2)/3)
start := lens
for i := range a {
start -= 3
if start < 0 {
start = 0
}
a[len(a)-i-1] = str[start:lens]
lens -= 3
}
return strings.Join(a, " ")
}
func TestVectorNums(t *testing.T) {
if testing.Short() {
return
}
var v Vector
c := int(0)
runtime.GC()
m0 := runtime.MemStats
v.Resize(memTestN, memTestN)
for i := 0; i < memTestN; i++ {
v.Set(i, c)
}
runtime.GC()
m := runtime.MemStats
v.Resize(0, 0)
runtime.GC()
n := m.Alloc - m0.Alloc
t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN)
}
func TestIntVectorNums(t *testing.T) {
if testing.Short() {
return
}
var v IntVector
c := int(0)
runtime.GC()
m0 := runtime.MemStats
v.Resize(memTestN, memTestN)
for i := 0; i < memTestN; i++ {
v.Set(i, c)
}
runtime.GC()
m := runtime.MemStats
v.Resize(0, 0)
runtime.GC()
n := m.Alloc - m0.Alloc
t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN)
}
func TestStringVectorNums(t *testing.T) {
if testing.Short() {
return
}
var v StringVector
c := ""
runtime.GC()
m0 := runtime.MemStats
v.Resize(memTestN, memTestN)
for i := 0; i < memTestN; i++ {
v.Set(i, c)
}
runtime.GC()
m := runtime.MemStats
v.Resize(0, 0)
runtime.GC()
n := m.Alloc - m0.Alloc
t.Logf("%T.Push(%#v), n = %s: Alloc/n = %.2f\n", v, c, s(memTestN), float64(n)/memTestN)
}
func BenchmarkVectorNums(b *testing.B) {
c := int(0)
var v Vector
b.StopTimer()
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
v.Push(c)
}
}
func BenchmarkIntVectorNums(b *testing.B) {
c := int(0)
var v IntVector
b.StopTimer()
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
v.Push(c)
}
}
func BenchmarkStringVectorNums(b *testing.B) {
c := ""
var v StringVector
b.StopTimer()
runtime.GC()
b.StartTimer()
for i := 0; i < b.N; i++ {
v.Push(c)
}
}

View file

@ -1,188 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CAUTION: If this file is not vector.go, it was generated
// automatically from vector.go - DO NOT EDIT in that case!
package vector
func (p *StringVector) realloc(length, capacity int) (b []string) {
if capacity < initialSize {
capacity = initialSize
}
if capacity < length {
capacity = length
}
b = make(StringVector, length, capacity)
copy(b, *p)
*p = b
return
}
// Insert n elements at position i.
func (p *StringVector) Expand(i, n int) {
a := *p
// make sure we have enough space
len0 := len(a)
len1 := len0 + n
if len1 <= cap(a) {
// enough space - just expand
a = a[0:len1]
} else {
// not enough space - double capacity
capb := cap(a) * 2
if capb < len1 {
// still not enough - use required length
capb = len1
}
// capb >= len1
a = p.realloc(len1, capb)
}
// make a hole
for j := len0 - 1; j >= i; j-- {
a[j+n] = a[j]
}
*p = a
}
// Insert n elements at the end of a vector.
func (p *StringVector) Extend(n int) { p.Expand(len(*p), n) }
// Resize changes the length and capacity of a vector.
// If the new length is shorter than the current length, Resize discards
// trailing elements. If the new length is longer than the current length,
// Resize adds the respective zero values for the additional elements. The capacity
// parameter is ignored unless the new length or capacity is longer than the current
// capacity. The resized vector's capacity may be larger than the requested capacity.
func (p *StringVector) Resize(length, capacity int) *StringVector {
a := *p
if length > cap(a) || capacity > cap(a) {
// not enough space or larger capacity requested explicitly
a = p.realloc(length, capacity)
} else if length < len(a) {
// clear trailing elements
for i := range a[length:] {
var zero string
a[length+i] = zero
}
}
*p = a[0:length]
return p
}
// Len returns the number of elements in the vector.
// Same as len(*p).
func (p *StringVector) Len() int { return len(*p) }
// Cap returns the capacity of the vector; that is, the
// maximum length the vector can grow without resizing.
// Same as cap(*p).
func (p *StringVector) Cap() int { return cap(*p) }
// At returns the i'th element of the vector.
func (p *StringVector) At(i int) string { return (*p)[i] }
// Set sets the i'th element of the vector to value x.
func (p *StringVector) Set(i int, x string) { (*p)[i] = x }
// Last returns the element in the vector of highest index.
func (p *StringVector) Last() string { return (*p)[len(*p)-1] }
// Copy makes a copy of the vector and returns it.
func (p *StringVector) Copy() StringVector {
arr := make(StringVector, len(*p))
copy(arr, *p)
return arr
}
// Insert inserts into the vector an element of value x before
// the current element at index i.
func (p *StringVector) Insert(i int, x string) {
p.Expand(i, 1)
(*p)[i] = x
}
// Delete deletes the i'th element of the vector. The gap is closed so the old
// element at index i+1 has index i afterwards.
func (p *StringVector) Delete(i int) {
a := *p
n := len(a)
copy(a[i:n-1], a[i+1:n])
var zero string
a[n-1] = zero // support GC, zero out entry
*p = a[0 : n-1]
}
// InsertVector inserts into the vector the contents of the vector
// x such that the 0th element of x appears at index i after insertion.
func (p *StringVector) InsertVector(i int, x *StringVector) {
b := *x
p.Expand(i, len(b))
copy((*p)[i:i+len(b)], b)
}
// Cut deletes elements i through j-1, inclusive.
func (p *StringVector) Cut(i, j int) {
a := *p
n := len(a)
m := n - (j - i)
copy(a[i:m], a[j:n])
for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector.
var zero string
a[k] = zero // support GC, zero out entries
}
*p = a[0:m]
}
// Slice returns a new sub-vector by slicing the old one to extract slice [i:j].
// The elements are copied. The original vector is unchanged.
func (p *StringVector) Slice(i, j int) *StringVector {
var s StringVector
s.realloc(j-i, 0) // will fail in Init() if j < i
copy(s, (*p)[i:j])
return &s
}
// Convenience wrappers
// Push appends x to the end of the vector.
func (p *StringVector) Push(x string) { p.Insert(len(*p), x) }
// Pop deletes the last element of the vector.
func (p *StringVector) Pop() string {
a := *p
i := len(a) - 1
x := a[i]
var zero string
a[i] = zero // support GC, zero out entry
*p = a[0:i]
return x
}
// AppendVector appends the entire vector x to the end of this vector.
func (p *StringVector) AppendVector(x *StringVector) { p.InsertVector(len(*p), x) }
// Swap exchanges the elements at indexes i and j.
func (p *StringVector) Swap(i, j int) {
a := *p
a[i], a[j] = a[j], a[i]
}
// Do calls function f for each element of the vector, in order.
// The behavior of Do is undefined if f changes *p.
func (p *StringVector) Do(f func(elem string)) {
for _, e := range *p {
f(e)
}
}

View file

@ -1,331 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CAUTION: If this file is not vector_test.go, it was generated
// automatically from vector_test.go - DO NOT EDIT in that case!
package vector
import "testing"
func TestStrZeroLen(t *testing.T) {
a := new(StringVector)
if a.Len() != 0 {
t.Errorf("%T: B1) expected 0, got %d", a, a.Len())
}
if len(*a) != 0 {
t.Errorf("%T: B2) expected 0, got %d", a, len(*a))
}
var b StringVector
if b.Len() != 0 {
t.Errorf("%T: B3) expected 0, got %d", b, b.Len())
}
if len(b) != 0 {
t.Errorf("%T: B4) expected 0, got %d", b, len(b))
}
}
func TestStrResize(t *testing.T) {
var a StringVector
checkSize(t, &a, 0, 0)
checkSize(t, a.Resize(0, 5), 0, 5)
checkSize(t, a.Resize(1, 0), 1, 5)
checkSize(t, a.Resize(10, 0), 10, 10)
checkSize(t, a.Resize(5, 0), 5, 10)
checkSize(t, a.Resize(3, 8), 3, 10)
checkSize(t, a.Resize(0, 100), 0, 100)
checkSize(t, a.Resize(11, 100), 11, 100)
}
func TestStrResize2(t *testing.T) {
var a StringVector
checkSize(t, &a, 0, 0)
a.Push(int2StrValue(1))
a.Push(int2StrValue(2))
a.Push(int2StrValue(3))
a.Push(int2StrValue(4))
checkSize(t, &a, 4, 4)
checkSize(t, a.Resize(10, 0), 10, 10)
for i := 4; i < a.Len(); i++ {
if a.At(i) != strzero {
t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, strzero, a.At(i))
}
}
for i := 4; i < len(a); i++ {
if a[i] != strzero {
t.Errorf("%T: expected a[%d] == %v; found %v", a, i, strzero, a[i])
}
}
}
func checkStrZero(t *testing.T, a *StringVector, i int) {
for j := 0; j < i; j++ {
if a.At(j) == strzero {
t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j))
}
if (*a)[j] == strzero {
t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j])
}
}
for ; i < a.Len(); i++ {
if a.At(i) != strzero {
t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, strzero, a.At(i))
}
if (*a)[i] != strzero {
t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, strzero, (*a)[i])
}
}
}
func TestStrTrailingElements(t *testing.T) {
var a StringVector
for i := 0; i < 10; i++ {
a.Push(int2StrValue(i + 1))
}
checkStrZero(t, &a, 10)
checkSize(t, &a, 10, 16)
checkSize(t, a.Resize(5, 0), 5, 16)
checkSize(t, a.Resize(10, 0), 10, 16)
checkStrZero(t, &a, 5)
}
func TestStrAccess(t *testing.T) {
const n = 100
var a StringVector
a.Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2StrValue(val(i)))
}
for i := 0; i < n; i++ {
if elem2StrValue(a.At(i)) != int2StrValue(val(i)) {
t.Error(i)
}
}
var b StringVector
b.Resize(n, 0)
for i := 0; i < n; i++ {
b[i] = int2StrValue(val(i))
}
for i := 0; i < n; i++ {
if elem2StrValue(b[i]) != int2StrValue(val(i)) {
t.Error(i)
}
}
}
func TestStrInsertDeleteClear(t *testing.T) {
const n = 100
var a StringVector
for i := 0; i < n; i++ {
if a.Len() != i {
t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i)
}
a.Insert(0, int2StrValue(val(i)))
if elem2StrValue(a.Last()) != int2StrValue(val(0)) {
t.Errorf("%T: B", a)
}
}
for i := n - 1; i >= 0; i-- {
if elem2StrValue(a.Last()) != int2StrValue(val(0)) {
t.Errorf("%T: C", a)
}
if elem2StrValue(a.At(0)) != int2StrValue(val(i)) {
t.Errorf("%T: D", a)
}
if elem2StrValue(a[0]) != int2StrValue(val(i)) {
t.Errorf("%T: D2", a)
}
a.Delete(0)
if a.Len() != i {
t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i)
}
}
if a.Len() != 0 {
t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a))
}
for i := 0; i < n; i++ {
a.Push(int2StrValue(val(i)))
if a.Len() != i+1 {
t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1)
}
if len(a) != i+1 {
t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1)
}
if elem2StrValue(a.Last()) != int2StrValue(val(i)) {
t.Errorf("%T: H", a)
}
}
a.Resize(0, 0)
if a.Len() != 0 {
t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a))
}
const m = 5
for j := 0; j < m; j++ {
a.Push(int2StrValue(j))
for i := 0; i < n; i++ {
x := val(i)
a.Push(int2StrValue(x))
if elem2StrValue(a.Pop()) != int2StrValue(x) {
t.Errorf("%T: J", a)
}
if a.Len() != j+1 {
t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1)
}
if len(a) != j+1 {
t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1)
}
}
}
if a.Len() != m {
t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m)
}
if len(a) != m {
t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m)
}
}
func verify_sliceStr(t *testing.T, x *StringVector, elt, i, j int) {
for k := i; k < j; k++ {
if elem2StrValue(x.At(k)) != int2StrValue(elt) {
t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt))
}
}
s := x.Slice(i, j)
for k, n := 0, j-i; k < n; k++ {
if elem2StrValue(s.At(k)) != int2StrValue(elt) {
t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2StrValue(x.At(k)), int2StrValue(elt))
}
}
}
func verify_patternStr(t *testing.T, x *StringVector, a, b, c int) {
n := a + b + c
if x.Len() != n {
t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n)
}
if len(*x) != n {
t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n)
}
verify_sliceStr(t, x, 0, 0, a)
verify_sliceStr(t, x, 1, a, a+b)
verify_sliceStr(t, x, 0, a+b, n)
}
func make_vectorStr(elt, len int) *StringVector {
x := new(StringVector).Resize(len, 0)
for i := 0; i < len; i++ {
x.Set(i, int2StrValue(elt))
}
return x
}
func TestStrInsertVector(t *testing.T) {
// 1
a := make_vectorStr(0, 0)
b := make_vectorStr(1, 10)
a.InsertVector(0, b)
verify_patternStr(t, a, 0, 10, 0)
// 2
a = make_vectorStr(0, 10)
b = make_vectorStr(1, 0)
a.InsertVector(5, b)
verify_patternStr(t, a, 5, 0, 5)
// 3
a = make_vectorStr(0, 10)
b = make_vectorStr(1, 3)
a.InsertVector(3, b)
verify_patternStr(t, a, 3, 3, 7)
// 4
a = make_vectorStr(0, 10)
b = make_vectorStr(1, 1000)
a.InsertVector(8, b)
verify_patternStr(t, a, 8, 1000, 2)
}
func TestStrDo(t *testing.T) {
const n = 25
const salt = 17
a := new(StringVector).Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2StrValue(salt*i))
}
count := 0
a.Do(func(e string) {
i := intf2StrValue(e)
if i != int2StrValue(count*salt) {
t.Error(tname(a), "value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(a), "should visit", n, "values; did visit", count)
}
b := new(StringVector).Resize(n, 0)
for i := 0; i < n; i++ {
(*b)[i] = int2StrValue(salt * i)
}
count = 0
b.Do(func(e string) {
i := intf2StrValue(e)
if i != int2StrValue(count*salt) {
t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(b), "b) should visit", n, "values; did visit", count)
}
var c StringVector
c.Resize(n, 0)
for i := 0; i < n; i++ {
c[i] = int2StrValue(salt * i)
}
count = 0
c.Do(func(e string) {
i := intf2StrValue(e)
if i != int2StrValue(count*salt) {
t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(c), "c) should visit", n, "values; did visit", count)
}
}
func TestStrVectorCopy(t *testing.T) {
// verify Copy() returns a copy, not simply a slice of the original vector
const Len = 10
var src StringVector
for i := 0; i < Len; i++ {
src.Push(int2StrValue(i * i))
}
dest := src.Copy()
for i := 0; i < Len; i++ {
src[i] = int2StrValue(-1)
v := elem2StrValue(dest[i])
if v != int2StrValue(i*i) {
t.Error(tname(src), "expected", i*i, "got", v)
}
}
}

View file

@ -1,188 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CAUTION: If this file is not vector.go, it was generated
// automatically from vector.go - DO NOT EDIT in that case!
package vector
func (p *Vector) realloc(length, capacity int) (b []interface{}) {
if capacity < initialSize {
capacity = initialSize
}
if capacity < length {
capacity = length
}
b = make(Vector, length, capacity)
copy(b, *p)
*p = b
return
}
// Insert n elements at position i.
func (p *Vector) Expand(i, n int) {
a := *p
// make sure we have enough space
len0 := len(a)
len1 := len0 + n
if len1 <= cap(a) {
// enough space - just expand
a = a[0:len1]
} else {
// not enough space - double capacity
capb := cap(a) * 2
if capb < len1 {
// still not enough - use required length
capb = len1
}
// capb >= len1
a = p.realloc(len1, capb)
}
// make a hole
for j := len0 - 1; j >= i; j-- {
a[j+n] = a[j]
}
*p = a
}
// Insert n elements at the end of a vector.
func (p *Vector) Extend(n int) { p.Expand(len(*p), n) }
// Resize changes the length and capacity of a vector.
// If the new length is shorter than the current length, Resize discards
// trailing elements. If the new length is longer than the current length,
// Resize adds the respective zero values for the additional elements. The capacity
// parameter is ignored unless the new length or capacity is longer than the current
// capacity. The resized vector's capacity may be larger than the requested capacity.
func (p *Vector) Resize(length, capacity int) *Vector {
a := *p
if length > cap(a) || capacity > cap(a) {
// not enough space or larger capacity requested explicitly
a = p.realloc(length, capacity)
} else if length < len(a) {
// clear trailing elements
for i := range a[length:] {
var zero interface{}
a[length+i] = zero
}
}
*p = a[0:length]
return p
}
// Len returns the number of elements in the vector.
// Same as len(*p).
func (p *Vector) Len() int { return len(*p) }
// Cap returns the capacity of the vector; that is, the
// maximum length the vector can grow without resizing.
// Same as cap(*p).
func (p *Vector) Cap() int { return cap(*p) }
// At returns the i'th element of the vector.
func (p *Vector) At(i int) interface{} { return (*p)[i] }
// Set sets the i'th element of the vector to value x.
func (p *Vector) Set(i int, x interface{}) { (*p)[i] = x }
// Last returns the element in the vector of highest index.
func (p *Vector) Last() interface{} { return (*p)[len(*p)-1] }
// Copy makes a copy of the vector and returns it.
func (p *Vector) Copy() Vector {
arr := make(Vector, len(*p))
copy(arr, *p)
return arr
}
// Insert inserts into the vector an element of value x before
// the current element at index i.
func (p *Vector) Insert(i int, x interface{}) {
p.Expand(i, 1)
(*p)[i] = x
}
// Delete deletes the i'th element of the vector. The gap is closed so the old
// element at index i+1 has index i afterwards.
func (p *Vector) Delete(i int) {
a := *p
n := len(a)
copy(a[i:n-1], a[i+1:n])
var zero interface{}
a[n-1] = zero // support GC, zero out entry
*p = a[0 : n-1]
}
// InsertVector inserts into the vector the contents of the vector
// x such that the 0th element of x appears at index i after insertion.
func (p *Vector) InsertVector(i int, x *Vector) {
b := *x
p.Expand(i, len(b))
copy((*p)[i:i+len(b)], b)
}
// Cut deletes elements i through j-1, inclusive.
func (p *Vector) Cut(i, j int) {
a := *p
n := len(a)
m := n - (j - i)
copy(a[i:m], a[j:n])
for k := m; k < n; k++ { //TODO(bflm) don't zero out the elements unless it's a Vector.
var zero interface{}
a[k] = zero // support GC, zero out entries
}
*p = a[0:m]
}
// Slice returns a new sub-vector by slicing the old one to extract slice [i:j].
// The elements are copied. The original vector is unchanged.
func (p *Vector) Slice(i, j int) *Vector {
var s Vector
s.realloc(j-i, 0) // will fail in Init() if j < i
copy(s, (*p)[i:j])
return &s
}
// Convenience wrappers
// Push appends x to the end of the vector.
func (p *Vector) Push(x interface{}) { p.Insert(len(*p), x) }
// Pop deletes the last element of the vector.
func (p *Vector) Pop() interface{} {
a := *p
i := len(a) - 1
x := a[i]
var zero interface{}
a[i] = zero // support GC, zero out entry
*p = a[0:i]
return x
}
// AppendVector appends the entire vector x to the end of this vector.
func (p *Vector) AppendVector(x *Vector) { p.InsertVector(len(*p), x) }
// Swap exchanges the elements at indexes i and j.
func (p *Vector) Swap(i, j int) {
a := *p
a[i], a[j] = a[j], a[i]
}
// Do calls function f for each element of the vector, in order.
// The behavior of Do is undefined if f changes *p.
func (p *Vector) Do(f func(elem interface{})) {
for _, e := range *p {
f(e)
}
}

View file

@ -1,331 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// CAUTION: If this file is not vector_test.go, it was generated
// automatically from vector_test.go - DO NOT EDIT in that case!
package vector
import "testing"
func TestZeroLen(t *testing.T) {
a := new(Vector)
if a.Len() != 0 {
t.Errorf("%T: B1) expected 0, got %d", a, a.Len())
}
if len(*a) != 0 {
t.Errorf("%T: B2) expected 0, got %d", a, len(*a))
}
var b Vector
if b.Len() != 0 {
t.Errorf("%T: B3) expected 0, got %d", b, b.Len())
}
if len(b) != 0 {
t.Errorf("%T: B4) expected 0, got %d", b, len(b))
}
}
func TestResize(t *testing.T) {
var a Vector
checkSize(t, &a, 0, 0)
checkSize(t, a.Resize(0, 5), 0, 5)
checkSize(t, a.Resize(1, 0), 1, 5)
checkSize(t, a.Resize(10, 0), 10, 10)
checkSize(t, a.Resize(5, 0), 5, 10)
checkSize(t, a.Resize(3, 8), 3, 10)
checkSize(t, a.Resize(0, 100), 0, 100)
checkSize(t, a.Resize(11, 100), 11, 100)
}
func TestResize2(t *testing.T) {
var a Vector
checkSize(t, &a, 0, 0)
a.Push(int2Value(1))
a.Push(int2Value(2))
a.Push(int2Value(3))
a.Push(int2Value(4))
checkSize(t, &a, 4, 4)
checkSize(t, a.Resize(10, 0), 10, 10)
for i := 4; i < a.Len(); i++ {
if a.At(i) != zero {
t.Errorf("%T: expected a.At(%d) == %v; found %v!", a, i, zero, a.At(i))
}
}
for i := 4; i < len(a); i++ {
if a[i] != zero {
t.Errorf("%T: expected a[%d] == %v; found %v", a, i, zero, a[i])
}
}
}
func checkZero(t *testing.T, a *Vector, i int) {
for j := 0; j < i; j++ {
if a.At(j) == zero {
t.Errorf("%T: 1 expected a.At(%d) == %d; found %v", a, j, j, a.At(j))
}
if (*a)[j] == zero {
t.Errorf("%T: 2 expected (*a)[%d] == %d; found %v", a, j, j, (*a)[j])
}
}
for ; i < a.Len(); i++ {
if a.At(i) != zero {
t.Errorf("%T: 3 expected a.At(%d) == %v; found %v", a, i, zero, a.At(i))
}
if (*a)[i] != zero {
t.Errorf("%T: 4 expected (*a)[%d] == %v; found %v", a, i, zero, (*a)[i])
}
}
}
func TestTrailingElements(t *testing.T) {
var a Vector
for i := 0; i < 10; i++ {
a.Push(int2Value(i + 1))
}
checkZero(t, &a, 10)
checkSize(t, &a, 10, 16)
checkSize(t, a.Resize(5, 0), 5, 16)
checkSize(t, a.Resize(10, 0), 10, 16)
checkZero(t, &a, 5)
}
func TestAccess(t *testing.T) {
const n = 100
var a Vector
a.Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2Value(val(i)))
}
for i := 0; i < n; i++ {
if elem2Value(a.At(i)) != int2Value(val(i)) {
t.Error(i)
}
}
var b Vector
b.Resize(n, 0)
for i := 0; i < n; i++ {
b[i] = int2Value(val(i))
}
for i := 0; i < n; i++ {
if elem2Value(b[i]) != int2Value(val(i)) {
t.Error(i)
}
}
}
func TestInsertDeleteClear(t *testing.T) {
const n = 100
var a Vector
for i := 0; i < n; i++ {
if a.Len() != i {
t.Errorf("%T: A) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: A) wrong len() %d (expected %d)", a, len(a), i)
}
a.Insert(0, int2Value(val(i)))
if elem2Value(a.Last()) != int2Value(val(0)) {
t.Errorf("%T: B", a)
}
}
for i := n - 1; i >= 0; i-- {
if elem2Value(a.Last()) != int2Value(val(0)) {
t.Errorf("%T: C", a)
}
if elem2Value(a.At(0)) != int2Value(val(i)) {
t.Errorf("%T: D", a)
}
if elem2Value(a[0]) != int2Value(val(i)) {
t.Errorf("%T: D2", a)
}
a.Delete(0)
if a.Len() != i {
t.Errorf("%T: E) wrong Len() %d (expected %d)", a, a.Len(), i)
}
if len(a) != i {
t.Errorf("%T: E) wrong len() %d (expected %d)", a, len(a), i)
}
}
if a.Len() != 0 {
t.Errorf("%T: F) wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: F) wrong len() %d (expected 0)", a, len(a))
}
for i := 0; i < n; i++ {
a.Push(int2Value(val(i)))
if a.Len() != i+1 {
t.Errorf("%T: G) wrong Len() %d (expected %d)", a, a.Len(), i+1)
}
if len(a) != i+1 {
t.Errorf("%T: G) wrong len() %d (expected %d)", a, len(a), i+1)
}
if elem2Value(a.Last()) != int2Value(val(i)) {
t.Errorf("%T: H", a)
}
}
a.Resize(0, 0)
if a.Len() != 0 {
t.Errorf("%T: I wrong Len() %d (expected 0)", a, a.Len())
}
if len(a) != 0 {
t.Errorf("%T: I wrong len() %d (expected 0)", a, len(a))
}
const m = 5
for j := 0; j < m; j++ {
a.Push(int2Value(j))
for i := 0; i < n; i++ {
x := val(i)
a.Push(int2Value(x))
if elem2Value(a.Pop()) != int2Value(x) {
t.Errorf("%T: J", a)
}
if a.Len() != j+1 {
t.Errorf("%T: K) wrong Len() %d (expected %d)", a, a.Len(), j+1)
}
if len(a) != j+1 {
t.Errorf("%T: K) wrong len() %d (expected %d)", a, len(a), j+1)
}
}
}
if a.Len() != m {
t.Errorf("%T: L) wrong Len() %d (expected %d)", a, a.Len(), m)
}
if len(a) != m {
t.Errorf("%T: L) wrong len() %d (expected %d)", a, len(a), m)
}
}
func verify_slice(t *testing.T, x *Vector, elt, i, j int) {
for k := i; k < j; k++ {
if elem2Value(x.At(k)) != int2Value(elt) {
t.Errorf("%T: M) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt))
}
}
s := x.Slice(i, j)
for k, n := 0, j-i; k < n; k++ {
if elem2Value(s.At(k)) != int2Value(elt) {
t.Errorf("%T: N) wrong [%d] element %v (expected %v)", x, k, elem2Value(x.At(k)), int2Value(elt))
}
}
}
func verify_pattern(t *testing.T, x *Vector, a, b, c int) {
n := a + b + c
if x.Len() != n {
t.Errorf("%T: O) wrong Len() %d (expected %d)", x, x.Len(), n)
}
if len(*x) != n {
t.Errorf("%T: O) wrong len() %d (expected %d)", x, len(*x), n)
}
verify_slice(t, x, 0, 0, a)
verify_slice(t, x, 1, a, a+b)
verify_slice(t, x, 0, a+b, n)
}
func make_vector(elt, len int) *Vector {
x := new(Vector).Resize(len, 0)
for i := 0; i < len; i++ {
x.Set(i, int2Value(elt))
}
return x
}
func TestInsertVector(t *testing.T) {
// 1
a := make_vector(0, 0)
b := make_vector(1, 10)
a.InsertVector(0, b)
verify_pattern(t, a, 0, 10, 0)
// 2
a = make_vector(0, 10)
b = make_vector(1, 0)
a.InsertVector(5, b)
verify_pattern(t, a, 5, 0, 5)
// 3
a = make_vector(0, 10)
b = make_vector(1, 3)
a.InsertVector(3, b)
verify_pattern(t, a, 3, 3, 7)
// 4
a = make_vector(0, 10)
b = make_vector(1, 1000)
a.InsertVector(8, b)
verify_pattern(t, a, 8, 1000, 2)
}
func TestDo(t *testing.T) {
const n = 25
const salt = 17
a := new(Vector).Resize(n, 0)
for i := 0; i < n; i++ {
a.Set(i, int2Value(salt*i))
}
count := 0
a.Do(func(e interface{}) {
i := intf2Value(e)
if i != int2Value(count*salt) {
t.Error(tname(a), "value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(a), "should visit", n, "values; did visit", count)
}
b := new(Vector).Resize(n, 0)
for i := 0; i < n; i++ {
(*b)[i] = int2Value(salt * i)
}
count = 0
b.Do(func(e interface{}) {
i := intf2Value(e)
if i != int2Value(count*salt) {
t.Error(tname(b), "b) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(b), "b) should visit", n, "values; did visit", count)
}
var c Vector
c.Resize(n, 0)
for i := 0; i < n; i++ {
c[i] = int2Value(salt * i)
}
count = 0
c.Do(func(e interface{}) {
i := intf2Value(e)
if i != int2Value(count*salt) {
t.Error(tname(c), "c) value at", count, "should be", count*salt, "not", i)
}
count++
})
if count != n {
t.Error(tname(c), "c) should visit", n, "values; did visit", count)
}
}
func TestVectorCopy(t *testing.T) {
// verify Copy() returns a copy, not simply a slice of the original vector
const Len = 10
var src Vector
for i := 0; i < Len; i++ {
src.Push(int2Value(i * i))
}
dest := src.Copy()
for i := 0; i < Len; i++ {
src[i] = int2Value(-1)
v := elem2Value(dest[i])
if v != int2Value(i*i) {
t.Error(tname(src), "expected", i*i, "got", v)
}
}
}

View file

@ -0,0 +1,38 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bcrypt
import (
"encoding/base64"
"os"
)
const alphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
var bcEncoding = base64.NewEncoding(alphabet)
func base64Encode(src []byte) []byte {
n := bcEncoding.EncodedLen(len(src))
dst := make([]byte, n)
bcEncoding.Encode(dst, src)
for dst[n-1] == '=' {
n--
}
return dst[:n]
}
func base64Decode(src []byte) ([]byte, os.Error) {
numOfEquals := 4 - (len(src) % 4)
for i := 0; i < numOfEquals; i++ {
src = append(src, '=')
}
dst := make([]byte, bcEncoding.DecodedLen(len(src)))
n, err := bcEncoding.Decode(dst, src)
if err != nil {
return nil, err
}
return dst[:n], nil
}

View file

@ -0,0 +1,282 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package bcrypt implements Provos and Mazières's bcrypt adapative hashing
// algorithm. See http://www.usenix.org/event/usenix99/provos/provos.pdf
package bcrypt
// The code is a port of Provos and Mazières's C implementation.
import (
"crypto/blowfish"
"crypto/rand"
"crypto/subtle"
"fmt"
"io"
"os"
"strconv"
)
const (
MinCost int = 4 // the minimum allowable cost as passed in to GenerateFromPassword
MaxCost int = 31 // the maximum allowable cost as passed in to GenerateFromPassword
DefaultCost int = 10 // the cost that will actually be set if a cost below MinCost is passed into GenerateFromPassword
)
// The error returned from CompareHashAndPassword when a password and hash do
// not match.
var MismatchedHashAndPasswordError = os.NewError("crypto/bcrypt: hashedPassword is not the hash of the given password")
// The error returned from CompareHashAndPassword when a hash is too short to
// be a bcrypt hash.
var HashTooShortError = os.NewError("crypto/bcrypt: hashedSecret too short to be a bcrypted password")
// The error returned from CompareHashAndPassword when a hash was created with
// a bcrypt algorithm newer than this implementation.
type HashVersionTooNewError byte
func (hv HashVersionTooNewError) String() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt algorithm version '%c' requested is newer than current version '%c'", byte(hv), majorVersion)
}
// The error returned from CompareHashAndPassword when a hash starts with something other than '$'
type InvalidHashPrefixError byte
func (ih InvalidHashPrefixError) String() string {
return fmt.Sprintf("crypto/bcrypt: bcrypt hashes must start with '$', but hashedSecret started with '%c'", byte(ih))
}
type InvalidCostError int
func (ic InvalidCostError) String() string {
return fmt.Sprintf("crypto/bcrypt: cost %d is outside allowed range (%d,%d)", int(ic), int(MinCost), int(MaxCost))
}
const (
majorVersion = '2'
minorVersion = 'a'
maxSaltSize = 16
maxCryptedHashSize = 23
encodedSaltSize = 22
encodedHashSize = 31
minHashSize = 59
)
// magicCipherData is an IV for the 64 Blowfish encryption calls in
// bcrypt(). It's the string "OrpheanBeholderScryDoubt" in big-endian bytes.
var magicCipherData = []byte{
0x4f, 0x72, 0x70, 0x68,
0x65, 0x61, 0x6e, 0x42,
0x65, 0x68, 0x6f, 0x6c,
0x64, 0x65, 0x72, 0x53,
0x63, 0x72, 0x79, 0x44,
0x6f, 0x75, 0x62, 0x74,
}
type hashed struct {
hash []byte
salt []byte
cost uint32 // allowed range is MinCost to MaxCost
major byte
minor byte
}
// GenerateFromPassword returns the bcrypt hash of the password at the given
// cost. If the cost given is less than MinCost, the cost will be set to
// MinCost, instead. Use CompareHashAndPassword, as defined in this package,
// to compare the returned hashed password with its cleartext version.
func GenerateFromPassword(password []byte, cost int) ([]byte, os.Error) {
p, err := newFromPassword(password, cost)
if err != nil {
return nil, err
}
return p.Hash(), nil
}
// CompareHashAndPassword compares a bcrypt hashed password with its possible
// plaintext equivalent. Note: Using bytes.Equal for this job is
// insecure. Returns nil on success, or an error on failure.
func CompareHashAndPassword(hashedPassword, password []byte) os.Error {
p, err := newFromHash(hashedPassword)
if err != nil {
return err
}
otherHash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return err
}
otherP := &hashed{otherHash, p.salt, p.cost, p.major, p.minor}
if subtle.ConstantTimeCompare(p.Hash(), otherP.Hash()) == 1 {
return nil
}
return MismatchedHashAndPasswordError
}
func newFromPassword(password []byte, cost int) (*hashed, os.Error) {
if cost < MinCost {
cost = DefaultCost
}
p := new(hashed)
p.major = majorVersion
p.minor = minorVersion
err := checkCost(cost)
if err != nil {
return nil, err
}
p.cost = uint32(cost)
unencodedSalt := make([]byte, maxSaltSize)
_, err = io.ReadFull(rand.Reader, unencodedSalt)
if err != nil {
return nil, err
}
p.salt = base64Encode(unencodedSalt)
hash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
return nil, err
}
p.hash = hash
return p, err
}
func newFromHash(hashedSecret []byte) (*hashed, os.Error) {
if len(hashedSecret) < minHashSize {
return nil, HashTooShortError
}
p := new(hashed)
n, err := p.decodeVersion(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
n, err = p.decodeCost(hashedSecret)
if err != nil {
return nil, err
}
hashedSecret = hashedSecret[n:]
// The "+2" is here because we'll have to append at most 2 '=' to the salt
// when base64 decoding it in expensiveBlowfishSetup().
p.salt = make([]byte, encodedSaltSize, encodedSaltSize+2)
copy(p.salt, hashedSecret[:encodedSaltSize])
hashedSecret = hashedSecret[encodedSaltSize:]
p.hash = make([]byte, len(hashedSecret))
copy(p.hash, hashedSecret)
return p, nil
}
func bcrypt(password []byte, cost uint32, salt []byte) ([]byte, os.Error) {
cipherData := make([]byte, len(magicCipherData))
copy(cipherData, magicCipherData)
c, err := expensiveBlowfishSetup(password, cost, salt)
if err != nil {
return nil, err
}
for i := 0; i < 24; i += 8 {
for j := 0; j < 64; j++ {
c.Encrypt(cipherData[i:i+8], cipherData[i:i+8])
}
}
// Bug compatibility with C bcrypt implementations. We only encode 23 of
// the 24 bytes encrypted.
hsh := base64Encode(cipherData[:maxCryptedHashSize])
return hsh, nil
}
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, os.Error) {
csalt, err := base64Decode(salt)
if err != nil {
return nil, err
}
// Bug compatibility with C bcrypt implementations. They use the trailing
// NULL in the key string during expansion.
ckey := append(key, 0)
c, err := blowfish.NewSaltedCipher(ckey, csalt)
if err != nil {
return nil, err
}
rounds := 1 << cost
for i := 0; i < rounds; i++ {
blowfish.ExpandKey(ckey, c)
blowfish.ExpandKey(csalt, c)
}
return c, nil
}
func (p *hashed) Hash() []byte {
arr := make([]byte, 60)
arr[0] = '$'
arr[1] = p.major
n := 2
if p.minor != 0 {
arr[2] = p.minor
n = 3
}
arr[n] = '$'
n += 1
copy(arr[n:], []byte(fmt.Sprintf("%02d", p.cost)))
n += 2
arr[n] = '$'
n += 1
copy(arr[n:], p.salt)
n += encodedSaltSize
copy(arr[n:], p.hash)
n += encodedHashSize
return arr[:n]
}
func (p *hashed) decodeVersion(sbytes []byte) (int, os.Error) {
if sbytes[0] != '$' {
return -1, InvalidHashPrefixError(sbytes[0])
}
if sbytes[1] > majorVersion {
return -1, HashVersionTooNewError(sbytes[1])
}
p.major = sbytes[1]
n := 3
if sbytes[2] != '$' {
p.minor = sbytes[2]
n++
}
return n, nil
}
// sbytes should begin where decodeVersion left off.
func (p *hashed) decodeCost(sbytes []byte) (int, os.Error) {
cost, err := strconv.Atoi(string(sbytes[0:2]))
if err != nil {
return -1, err
}
err = checkCost(cost)
if err != nil {
return -1, err
}
p.cost = uint32(cost)
return 3, nil
}
func (p *hashed) String() string {
return fmt.Sprintf("&{hash: %#v, salt: %#v, cost: %d, major: %c, minor: %c}", string(p.hash), p.salt, p.cost, p.major, p.minor)
}
func checkCost(cost int) os.Error {
if cost < MinCost || cost > MaxCost {
return InvalidCostError(cost)
}
return nil
}

View file

@ -0,0 +1,195 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bcrypt
import (
"bytes"
"os"
"testing"
)
func TestBcryptingIsEasy(t *testing.T) {
pass := []byte("mypassword")
hp, err := GenerateFromPassword(pass, 0)
if err != nil {
t.Fatalf("GenerateFromPassword error: %s", err)
}
if CompareHashAndPassword(hp, pass) != nil {
t.Errorf("%v should hash %s correctly", hp, pass)
}
notPass := "notthepass"
err = CompareHashAndPassword(hp, []byte(notPass))
if err != MismatchedHashAndPasswordError {
t.Errorf("%v and %s should be mismatched", hp, notPass)
}
}
func TestBcryptingIsCorrect(t *testing.T) {
pass := []byte("allmine")
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
expectedHash := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
hash, err := bcrypt(pass, 10, salt)
if err != nil {
t.Fatalf("bcrypt blew up: %v", err)
}
if !bytes.HasSuffix(expectedHash, hash) {
t.Errorf("%v should be the suffix of %v", hash, expectedHash)
}
h, err := newFromHash(expectedHash)
if err != nil {
t.Errorf("Unable to parse %s: %v", string(expectedHash), err)
}
// This is not the safe way to compare these hashes. We do this only for
// testing clarity. Use bcrypt.CompareHashAndPassword()
if err == nil && !bytes.Equal(expectedHash, h.Hash()) {
t.Errorf("Parsed hash %v should equal %v", h.Hash(), expectedHash)
}
}
func TestTooLongPasswordsWork(t *testing.T) {
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
// One byte over the usual 56 byte limit that blowfish has
tooLongPass := []byte("012345678901234567890123456789012345678901234567890123456")
tooLongExpected := []byte("$2a$10$XajjQvNhvvRt5GSeFk1xFe5l47dONXg781AmZtd869sO8zfsHuw7C")
hash, err := bcrypt(tooLongPass, 10, salt)
if err != nil {
t.Fatalf("bcrypt blew up on long password: %v", err)
}
if !bytes.HasSuffix(tooLongExpected, hash) {
t.Errorf("%v should be the suffix of %v", hash, tooLongExpected)
}
}
type InvalidHashTest struct {
err os.Error
hash []byte
}
var invalidTests = []InvalidHashTest{
{HashTooShortError, []byte("$2a$10$fooo")},
{HashTooShortError, []byte("$2a")},
{HashVersionTooNewError('3'), []byte("$3a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
{InvalidHashPrefixError('%'), []byte("%2a$10$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
{InvalidCostError(32), []byte("$2a$32$sssssssssssssssssssssshhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh")},
}
func TestInvalidHashErrors(t *testing.T) {
check := func(name string, expected, err os.Error) {
if err == nil {
t.Errorf("%s: Should have returned an error", name)
}
if err != nil && err != expected {
t.Errorf("%s gave err %v but should have given %v", name, err.String(), expected.String())
}
}
for _, iht := range invalidTests {
_, err := newFromHash(iht.hash)
check("newFromHash", iht.err, err)
err = CompareHashAndPassword(iht.hash, []byte("anything"))
check("CompareHashAndPassword", iht.err, err)
}
}
func TestUnpaddedBase64Encoding(t *testing.T) {
original := []byte{101, 201, 101, 75, 19, 227, 199, 20, 239, 236, 133, 32, 30, 109, 243, 30}
encodedOriginal := []byte("XajjQvNhvvRt5GSeFk1xFe")
encoded := base64Encode(original)
if !bytes.Equal(encodedOriginal, encoded) {
t.Errorf("Encoded %v should have equaled %v", encoded, encodedOriginal)
}
decoded, err := base64Decode(encodedOriginal)
if err != nil {
t.Fatalf("base64Decode blew up: %s", err)
}
if !bytes.Equal(decoded, original) {
t.Errorf("Decoded %v should have equaled %v", decoded, original)
}
}
func TestCost(t *testing.T) {
if testing.Short() {
return
}
pass := []byte("mypassword")
for c := 0; c < MinCost; c++ {
p, _ := newFromPassword(pass, c)
if p.cost != uint32(DefaultCost) {
t.Errorf("newFromPassword should default costs below %d to %d, but was %d", MinCost, DefaultCost, p.cost)
}
}
p, _ := newFromPassword(pass, 14)
if p.cost != 14 {
t.Errorf("newFromPassword should default cost to 14, but was %d", p.cost)
}
hp, _ := newFromHash(p.Hash())
if p.cost != hp.cost {
t.Errorf("newFromHash should maintain the cost at %d, but was %d", p.cost, hp.cost)
}
_, err := newFromPassword(pass, 32)
if err == nil {
t.Fatalf("newFromPassword: should return a cost error")
}
if err != InvalidCostError(32) {
t.Errorf("newFromPassword: should return cost error, got %#v", err)
}
}
func TestCostReturnsWithLeadingZeroes(t *testing.T) {
hp, _ := newFromPassword([]byte("abcdefgh"), 7)
cost := hp.Hash()[4:7]
expected := []byte("07$")
if !bytes.Equal(expected, cost) {
t.Errorf("single digit costs in hash should have leading zeros: was %v instead of %v", cost, expected)
}
}
func TestMinorNotRequired(t *testing.T) {
noMinorHash := []byte("$2$10$XajjQvNhvvRt5GSeFk1xFeyqRrsxkhBkUiQeg0dt.wU1qD4aFDcga")
h, err := newFromHash(noMinorHash)
if err != nil {
t.Fatalf("No minor hash blew up: %s", err)
}
if h.minor != 0 {
t.Errorf("Should leave minor version at 0, but was %d", h.minor)
}
if !bytes.Equal(noMinorHash, h.Hash()) {
t.Errorf("Should generate hash %v, but created %v", noMinorHash, h.Hash())
}
}
func BenchmarkEqual(b *testing.B) {
b.StopTimer()
passwd := []byte("somepasswordyoulike")
hash, _ := GenerateFromPassword(passwd, 10)
b.StartTimer()
for i := 0; i < b.N; i++ {
CompareHashAndPassword(hash, passwd)
}
}
func BenchmarkGeneration(b *testing.B) {
b.StopTimer()
passwd := []byte("mylongpassword1234")
b.StartTimer()
for i := 0; i < b.N; i++ {
GenerateFromPassword(passwd, 10)
}
}

View file

@ -4,13 +4,12 @@
package blowfish
func expandKey(key []byte, c *Cipher) {
copy(c.p[0:], p[0:])
copy(c.s0[0:], s0[0:])
copy(c.s1[0:], s1[0:])
copy(c.s2[0:], s2[0:])
copy(c.s3[0:], s3[0:])
// ExpandKey performs a key expansion on the given *Cipher. Specifically, it
// performs the Blowfish algorithm's key schedule which sets up the *Cipher's
// pi and substitution tables for calls to Encrypt. This is used, primarily,
// by the bcrypt package to reuse the Blowfish key schedule during its
// set up. It's unlikely that you need to use this directly.
func ExpandKey(key []byte, c *Cipher) {
j := 0
for i := 0; i < 18; i++ {
var d uint32
@ -48,6 +47,98 @@ func expandKey(key []byte, c *Cipher) {
}
}
// This is similar to ExpandKey, but folds the salt during the key
// schedule. While ExpandKey is essentially expandKeyWithSalt with an all-zero
// salt passed in, reusing ExpandKey turns out to be a place of inefficiency
// and specializing it here is useful.
func expandKeyWithSalt(key []byte, salt []byte, c *Cipher) {
j := 0
expandedKey := make([]uint32, 18)
for i := 0; i < 18; i++ {
var d uint32
for k := 0; k < 4; k++ {
d = d<<8 | uint32(key[j])&0x000000FF
j++
if j >= len(key) {
j = 0
}
}
expandedKey[i] = d
c.p[i] ^= d
}
j = 0
expandedSalt := make([]uint32, 18)
for i := 0; i < 18; i++ {
var d uint32
for k := 0; k < 4; k++ {
d = d<<8 | uint32(salt[j])&0x000000FF
j++
if j >= len(salt) {
j = 0
}
}
expandedSalt[i] = d
}
var l, r uint32
for i := 0; i < 18; i += 2 {
l ^= expandedSalt[i&2]
r ^= expandedSalt[(i&2)+1]
l, r = encryptBlock(l, r, c)
c.p[i], c.p[i+1] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s0[i], c.s0[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s0[i+2], c.s0[i+3] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s1[i], c.s1[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s1[i+2], c.s1[i+3] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s2[i], c.s2[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s2[i+2], c.s2[i+3] = l, r
}
for i := 0; i < 256; i += 4 {
l ^= expandedSalt[2]
r ^= expandedSalt[3]
l, r = encryptBlock(l, r, c)
c.s3[i], c.s3[i+1] = l, r
l ^= expandedSalt[0]
r ^= expandedSalt[1]
l, r = encryptBlock(l, r, c)
c.s3[i+2], c.s3[i+3] = l, r
}
}
func encryptBlock(l, r uint32, c *Cipher) (uint32, uint32) {
xl, xr := l, r
xl ^= c.p[0]

View file

@ -190,3 +190,21 @@ func TestCipherDecrypt(t *testing.T) {
}
}
}
func TestSaltedCipherKeyLength(t *testing.T) {
var key []byte
for i := 0; i < 4; i++ {
_, err := NewSaltedCipher(key, []byte{'a'})
if err != KeySizeError(i) {
t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(i))
}
key = append(key, 'a')
}
// A 57-byte key. One over the typical blowfish restriction.
key = []byte("012345678901234567890123456789012345678901234567890123456")
_, err := NewSaltedCipher(key, []byte{'a'})
if err != nil {
t.Errorf("NewSaltedCipher with long key, gave error %#v", err)
}
}

View file

@ -31,12 +31,28 @@ func (k KeySizeError) String() string {
// NewCipher creates and returns a Cipher.
// The key argument should be the Blowfish key, 4 to 56 bytes.
func NewCipher(key []byte) (*Cipher, os.Error) {
var result Cipher
k := len(key)
if k < 4 || k > 56 {
return nil, KeySizeError(k)
}
initCipher(key, &result)
ExpandKey(key, &result)
return &result, nil
}
// NewSaltedCipher creates a returns a Cipher that folds a salt into its key
// schedule. For most purposes, NewCipher, instead of NewSaltedCipher, is
// sufficient and desirable. For bcrypt compatiblity, the key can be over 56
// bytes.
func NewSaltedCipher(key, salt []byte) (*Cipher, os.Error) {
var result Cipher
expandKey(key, &result)
k := len(key)
if k < 4 {
return nil, KeySizeError(k)
}
initCipher(key, &result)
expandKeyWithSalt(key, salt, &result)
return &result, nil
}
@ -77,3 +93,11 @@ func (c *Cipher) Reset() {
zero(c.s2[0:])
zero(c.s3[0:])
}
func initCipher(key []byte, c *Cipher) {
copy(c.p[0:], p[0:])
copy(c.s0[0:], s0[0:])
copy(c.s1[0:], s1[0:])
copy(c.s2[0:], s2[0:])
copy(c.s3[0:], s3[0:])
}

View file

@ -295,7 +295,7 @@ func TestBaseMult(t *testing.T) {
}
x, y := p224.ScalarBaseMult(k.Bytes())
if fmt.Sprintf("%x", x) != e.x || fmt.Sprintf("%x", y) != e.y {
t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%s, %s)", i, e.k, x, y, e.x, e.y)
t.Errorf("%d: bad output for k=%s: got (%x, %s), want (%x, %s)", i, e.k, x, y, e.x, e.y)
}
if testing.Short() && i > 5 {
break

View file

@ -15,7 +15,7 @@ func TestOCSPDecode(t *testing.T) {
t.Error(err)
}
expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, Weekday: 0, ZoneOffset: 0, Zone: "UTC"}}
expected := Response{Status: 0, SerialNumber: []byte{0x1, 0xd0, 0xfa}, RevocationReason: 0, ThisUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 15, Minute: 1, Second: 5, ZoneOffset: 0, Zone: "UTC"}, NextUpdate: &time.Time{Year: 2010, Month: 7, Day: 7, Hour: 18, Minute: 35, Second: 17, ZoneOffset: 0, Zone: "UTC"}}
if !reflect.DeepEqual(resp.ThisUpdate, resp.ThisUpdate) {
t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate)

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd linux openbsd
// Unix cryptographically secure pseudorandom number
// generator.

View file

@ -7,8 +7,10 @@ package tls
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/hmac"
"crypto/rc4"
"crypto/sha1"
"crypto/x509"
"hash"
"os"
@ -23,7 +25,7 @@ type keyAgreement interface {
// ServerKeyExchange message, generateServerKeyExchange can return nil,
// nil.
generateServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, os.Error)
processClientKeyExchange(*Config, *clientKeyExchangeMsg) ([]byte, os.Error)
processClientKeyExchange(*Config, *clientKeyExchangeMsg, uint16) ([]byte, os.Error)
// On the client side, the next two methods are called in order.
@ -46,14 +48,16 @@ type cipherSuite struct {
// and point format that we can handle.
elliptic bool
cipher func(key, iv []byte, isRead bool) interface{}
mac func(macKey []byte) hash.Hash
mac func(version uint16, macKey []byte) macFunction
}
var cipherSuites = map[uint16]*cipherSuite{
TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, hmacSHA1},
TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, hmacSHA1},
TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, hmacSHA1},
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, hmacSHA1},
TLS_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, rsaKA, false, cipherRC4, macSHA1},
TLS_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, rsaKA, false, cipher3DES, macSHA1},
TLS_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, rsaKA, false, cipherAES, macSHA1},
TLS_ECDHE_RSA_WITH_RC4_128_SHA: &cipherSuite{16, 20, 0, ecdheRSAKA, true, cipherRC4, macSHA1},
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: &cipherSuite{24, 20, 8, ecdheRSAKA, true, cipher3DES, macSHA1},
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: &cipherSuite{16, 20, 16, ecdheRSAKA, true, cipherAES, macSHA1},
}
func cipherRC4(key, iv []byte, isRead bool) interface{} {
@ -61,6 +65,14 @@ func cipherRC4(key, iv []byte, isRead bool) interface{} {
return cipher
}
func cipher3DES(key, iv []byte, isRead bool) interface{} {
block, _ := des.NewTripleDESCipher(key)
if isRead {
return cipher.NewCBCDecrypter(block, iv)
}
return cipher.NewCBCEncrypter(block, iv)
}
func cipherAES(key, iv []byte, isRead bool) interface{} {
block, _ := aes.NewCipher(key)
if isRead {
@ -69,8 +81,75 @@ func cipherAES(key, iv []byte, isRead bool) interface{} {
return cipher.NewCBCEncrypter(block, iv)
}
func hmacSHA1(key []byte) hash.Hash {
return hmac.NewSHA1(key)
// macSHA1 returns a macFunction for the given protocol version.
func macSHA1(version uint16, key []byte) macFunction {
if version == versionSSL30 {
mac := ssl30MAC{
h: sha1.New(),
key: make([]byte, len(key)),
}
copy(mac.key, key)
return mac
}
return tls10MAC{hmac.NewSHA1(key)}
}
type macFunction interface {
Size() int
MAC(seq, data []byte) []byte
}
// ssl30MAC implements the SSLv3 MAC function, as defined in
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1
type ssl30MAC struct {
h hash.Hash
key []byte
}
func (s ssl30MAC) Size() int {
return s.h.Size()
}
var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}
var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
func (s ssl30MAC) MAC(seq, record []byte) []byte {
padLength := 48
if s.h.Size() == 20 {
padLength = 40
}
s.h.Reset()
s.h.Write(s.key)
s.h.Write(ssl30Pad1[:padLength])
s.h.Write(seq)
s.h.Write(record[:1])
s.h.Write(record[3:5])
s.h.Write(record[recordHeaderLen:])
digest := s.h.Sum()
s.h.Reset()
s.h.Write(s.key)
s.h.Write(ssl30Pad2[:padLength])
s.h.Write(digest)
return s.h.Sum()
}
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, section 6.2.3.
type tls10MAC struct {
h hash.Hash
}
func (s tls10MAC) Size() int {
return s.h.Size()
}
func (s tls10MAC) MAC(seq, record []byte) []byte {
s.h.Reset()
s.h.Write(seq)
s.h.Write(record)
return s.h.Sum()
}
func rsaKA() keyAgreement {
@ -95,8 +174,10 @@ func mutualCipherSuite(have []uint16, want uint16) (suite *cipherSuite, id uint1
// A list of the possible cipher suite ids. Taken from
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml
const (
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
)

View file

@ -9,7 +9,7 @@ import (
"crypto/rsa"
"crypto/x509"
"io"
"io/ioutil"
"strings"
"sync"
"time"
)
@ -20,8 +20,11 @@ const (
recordHeaderLen = 5 // record header length
maxHandshake = 65536 // maximum handshake we support (protocol max is 16 MB)
minVersion = 0x0301 // minimum supported version - TLS 1.0
maxVersion = 0x0301 // maximum supported version - TLS 1.0
versionSSL30 = 0x0300
versionTLS10 = 0x0301
minVersion = versionSSL30
maxVersion = versionTLS10
)
// TLS record types.
@ -98,6 +101,10 @@ type ConnectionState struct {
NegotiatedProtocol string
NegotiatedProtocolIsMutual bool
// ServerName contains the server name indicated by the client, if any.
// (Only valid for server connections.)
ServerName string
// the certificate chain that was presented by the other side
PeerCertificates []*x509.Certificate
// the verified certificate chains built from PeerCertificates.
@ -121,6 +128,14 @@ type Config struct {
// Server configurations must include at least one certificate.
Certificates []Certificate
// NameToCertificate maps from a certificate name to an element of
// Certificates. Note that a certificate name can be of the form
// '*.example.com' and so doesn't have to be a domain name as such.
// See Config.BuildNameToCertificate
// The nil value causes the first element of Certificates to be used
// for all connections.
NameToCertificate map[string]*Certificate
// RootCAs defines the set of root certificate authorities
// that clients use when verifying server certificates.
// If RootCAs is nil, TLS uses the host's root CA set.
@ -139,6 +154,14 @@ type Config struct {
// anything more than self-signed.
AuthenticateClient bool
// InsecureSkipVerify controls whether a client verifies the
// server's certificate chain and host name.
// If InsecureSkipVerify is true, TLS accepts any certificate
// presented by the server and any host name in that certificate.
// In this mode, TLS is susceptible to man-in-the-middle attacks.
// This should be used only for testing.
InsecureSkipVerify bool
// CipherSuites is a list of supported cipher suites. If CipherSuites
// is nil, TLS uses a list of suites supported by the implementation.
CipherSuites []uint16
@ -176,6 +199,59 @@ func (c *Config) cipherSuites() []uint16 {
return s
}
// getCertificateForName returns the best certificate for the given name,
// defaulting to the first element of c.Certificates if there are no good
// options.
func (c *Config) getCertificateForName(name string) *Certificate {
if len(c.Certificates) == 1 || c.NameToCertificate == nil {
// There's only one choice, so no point doing any work.
return &c.Certificates[0]
}
name = strings.ToLower(name)
for len(name) > 0 && name[len(name)-1] == '.' {
name = name[:len(name)-1]
}
if cert, ok := c.NameToCertificate[name]; ok {
return cert
}
// try replacing labels in the name with wildcards until we get a
// match.
labels := strings.Split(name, ".")
for i := range labels {
labels[i] = "*"
candidate := strings.Join(labels, ".")
if cert, ok := c.NameToCertificate[candidate]; ok {
return cert
}
}
// If nothing matches, return the first certificate.
return &c.Certificates[0]
}
// BuildNameToCertificate parses c.Certificates and builds c.NameToCertificate
// from the CommonName and SubjectAlternateName fields of each of the leaf
// certificates.
func (c *Config) BuildNameToCertificate() {
c.NameToCertificate = make(map[string]*Certificate)
for i := range c.Certificates {
cert := &c.Certificates[i]
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
continue
}
if len(x509Cert.Subject.CommonName) > 0 {
c.NameToCertificate[x509Cert.Subject.CommonName] = cert
}
for _, san := range x509Cert.DNSNames {
c.NameToCertificate[san] = cert
}
}
}
// A Certificate is a chain of one or more certificates, leaf first.
type Certificate struct {
Certificate [][]byte
@ -215,15 +291,6 @@ func defaultConfig() *Config {
return &emptyConfig
}
// Possible certificate files; stop after finding one.
// On OS X we should really be using the Directory Services keychain
// but that requires a lot of Mach goo to get at. Instead we use
// the same root set that curl uses.
var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Linux etc
"/usr/share/curl/curl-ca-bundle.crt", // OS X
}
var once sync.Once
func defaultRoots() *x509.CertPool {
@ -241,21 +308,10 @@ func initDefaults() {
initDefaultCipherSuites()
}
var varDefaultRoots *x509.CertPool
func initDefaultRoots() {
roots := x509.NewCertPool()
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
break
}
}
varDefaultRoots = roots
}
var varDefaultCipherSuites []uint16
var (
varDefaultRoots *x509.CertPool
varDefaultCipherSuites []uint16
)
func initDefaultCipherSuites() {
varDefaultCipherSuites = make([]uint16, len(cipherSuites))

View file

@ -11,7 +11,6 @@ import (
"crypto/cipher"
"crypto/subtle"
"crypto/x509"
"hash"
"io"
"net"
"os"
@ -37,6 +36,8 @@ type Conn struct {
// verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate
// serverName contains the server name indicated by the client, if any.
serverName string
clientProtocol string
clientProtocolFallback bool
@ -108,18 +109,20 @@ func (c *Conn) SetWriteTimeout(nsec int64) os.Error {
// connection, either sending or receiving.
type halfConn struct {
sync.Mutex
cipher interface{} // cipher algorithm
mac hash.Hash // MAC algorithm
seq [8]byte // 64-bit sequence number
bfree *block // list of free blocks
version uint16 // protocol version
cipher interface{} // cipher algorithm
mac macFunction
seq [8]byte // 64-bit sequence number
bfree *block // list of free blocks
nextCipher interface{} // next encryption state
nextMac hash.Hash // next MAC algorithm
nextMac macFunction // next MAC algorithm
}
// prepareCipherSpec sets the encryption and MAC states
// that a subsequent changeCipherSpec will use.
func (hc *halfConn) prepareCipherSpec(cipher interface{}, mac hash.Hash) {
func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) {
hc.version = version
hc.nextCipher = cipher
hc.nextMac = mac
}
@ -197,6 +200,22 @@ func removePadding(payload []byte) ([]byte, byte) {
return payload[:len(payload)-int(toRemove)], good
}
// removePaddingSSL30 is a replacement for removePadding in the case that the
// protocol version is SSLv3. In this version, the contents of the padding
// are random and cannot be checked.
func removePaddingSSL30(payload []byte) ([]byte, byte) {
if len(payload) < 1 {
return payload, 0
}
paddingLen := int(payload[len(payload)-1]) + 1
if paddingLen > len(payload) {
return payload, 0
}
return payload[:len(payload)-paddingLen], 255
}
func roundUp(a, b int) int {
return a + (b-a%b)%b
}
@ -226,7 +245,11 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
}
c.CryptBlocks(payload, payload)
payload, paddingGood = removePadding(payload)
if hc.version == versionSSL30 {
payload, paddingGood = removePaddingSSL30(payload)
} else {
payload, paddingGood = removePadding(payload)
}
b.resize(recordHeaderLen + len(payload))
// note that we still have a timing side-channel in the
@ -256,13 +279,10 @@ func (hc *halfConn) decrypt(b *block) (bool, alert) {
b.data[4] = byte(n)
b.resize(recordHeaderLen + n)
remoteMAC := payload[n:]
hc.mac.Reset()
hc.mac.Write(hc.seq[0:])
localMAC := hc.mac.MAC(hc.seq[0:], b.data)
hc.incSeq()
hc.mac.Write(b.data)
if subtle.ConstantTimeCompare(hc.mac.Sum(), remoteMAC) != 1 || paddingGood != 255 {
if subtle.ConstantTimeCompare(localMAC, remoteMAC) != 1 || paddingGood != 255 {
return false, alertBadRecordMAC
}
}
@ -291,11 +311,9 @@ func padToBlockSize(payload []byte, blockSize int) (prefix, finalBlock []byte) {
func (hc *halfConn) encrypt(b *block) (bool, alert) {
// mac
if hc.mac != nil {
hc.mac.Reset()
hc.mac.Write(hc.seq[0:])
mac := hc.mac.MAC(hc.seq[0:], b.data)
hc.incSeq()
hc.mac.Write(b.data)
mac := hc.mac.Sum()
n := len(b.data)
b.resize(n + len(mac))
copy(b.data[n:], mac)
@ -470,6 +488,19 @@ Again:
if n > maxCiphertext {
return c.sendAlert(alertRecordOverflow)
}
if !c.haveVers {
// First message, be extra suspicious:
// this might not be a TLS client.
// Bail out before reading a full 'body', if possible.
// The current max version is 3.1.
// If the version is >= 16.0, it's probably not real.
// Similarly, a clientHello message encodes in
// well under a kilobyte. If the length is >= 12 kB,
// it's probably not real.
if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 {
return c.sendAlert(alertUnexpectedMessage)
}
}
if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
if err == os.EOF {
err = io.ErrUnexpectedEOF
@ -627,7 +658,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) {
if c.err != nil {
return nil, c.err
}
c.readRecord(recordTypeHandshake)
if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
}
}
data := c.hand.Bytes()
@ -640,7 +673,9 @@ func (c *Conn) readHandshake() (interface{}, os.Error) {
if c.err != nil {
return nil, c.err
}
c.readRecord(recordTypeHandshake)
if err := c.readRecord(recordTypeHandshake); err != nil {
return nil, err
}
}
data = c.hand.Next(4 + n)
var m handshakeMessage
@ -731,10 +766,18 @@ func (c *Conn) Read(b []byte) (n int, err os.Error) {
// Close closes the connection.
func (c *Conn) Close() os.Error {
if err := c.Handshake(); err != nil {
var alertErr os.Error
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
if c.handshakeComplete {
alertErr = c.sendAlert(alertCloseNotify)
}
if err := c.conn.Close(); err != nil {
return err
}
return c.sendAlert(alertCloseNotify)
return alertErr
}
// Handshake runs the client or server handshake
@ -769,6 +812,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates
state.VerifiedChains = c.verifiedChains
state.ServerName = c.serverName
}
return state

View file

@ -50,3 +50,57 @@ func TestRemovePadding(t *testing.T) {
}
}
}
var certExampleCom = `308201403081eda003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313138353835325a170d3132303933303138353835325a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31a301830160603551d11040f300d820b6578616d706c652e636f6d300b06092a864886f70d0101050341001a0b419d2c74474c6450654e5f10b32bf426ffdf55cad1c52602e7a9151513a3424c70f5960dcd682db0c33769cc1daa3fcdd3db10809d2392ed4a1bf50ced18`
var certWildcardExampleCom = `308201423081efa003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303034365a170d3132303933303139303034365a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31c301a30180603551d110411300f820d2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001676f0c9e7c33c1b656ed5a6476c4e2ee9ec8e62df7407accb1875272b2edd0a22096cb2c22598d11604104d604f810eb4b5987ca6bb319c7e6ce48725c54059`
var certFooExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303131345a170d3132303933303139303131345a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f666f6f2e6578616d706c652e636f6d300b06092a864886f70d010105034100646a2a51f2aa2477add854b462cf5207ba16d3213ffb5d3d0eed473fbf09935019192d1d5b8ca6a2407b424cf04d97c4cd9197c83ecf81f0eab9464a1109d09f`
var certDoubleWildcardExampleCom = `308201443081f1a003020102020101300b06092a864886f70d010105301e311c301a060355040a131354657374696e67204365727469666963617465301e170d3131313030313139303134315a170d3132303933303139303134315a301e311c301a060355040a131354657374696e67204365727469666963617465305a300b06092a864886f70d010101034b003048024100bced6e32368599eeddf18796bfd03958a154f87e5b084f96e85136a56b886733592f493f0fc68b0d6b3551781cb95e13c5de458b28d6fb60d20a9129313261410203010001a31e301c301a0603551d1104133011820f2a2e2a2e6578616d706c652e636f6d300b06092a864886f70d0101050341001c3de267975f56ef57771c6218ef95ecc65102e57bd1defe6f7efea90d9b26cf40de5bd7ad75e46201c7f2a92aaa3e907451e9409f65e28ddb6db80d726290f6`
func TestCertificateSelection(t *testing.T) {
config := Config{
Certificates: []Certificate{
{
Certificate: [][]byte{fromHex(certExampleCom)},
},
{
Certificate: [][]byte{fromHex(certWildcardExampleCom)},
},
{
Certificate: [][]byte{fromHex(certFooExampleCom)},
},
{
Certificate: [][]byte{fromHex(certDoubleWildcardExampleCom)},
},
},
}
config.BuildNameToCertificate()
pointerToIndex := func(c *Certificate) int {
for i := range config.Certificates {
if c == &config.Certificates[i] {
return i
}
}
return -1
}
if n := pointerToIndex(config.getCertificateForName("example.com")); n != 0 {
t.Errorf("example.com returned certificate %d, not 0", n)
}
if n := pointerToIndex(config.getCertificateForName("bar.example.com")); n != 1 {
t.Errorf("bar.example.com returned certificate %d, not 1", n)
}
if n := pointerToIndex(config.getCertificateForName("foo.example.com")); n != 2 {
t.Errorf("foo.example.com returned certificate %d, not 2", n)
}
if n := pointerToIndex(config.getCertificateForName("foo.bar.example.com")); n != 3 {
t.Errorf("foo.bar.example.com returned certificate %d, not 3", n)
}
if n := pointerToIndex(config.getCertificateForName("foo.bar.baz.example.com")); n != 0 {
t.Errorf("foo.bar.baz.example.com returned certificate %d, not 0", n)
}
}

View file

@ -14,7 +14,7 @@ import (
)
func (c *Conn) clientHandshake() os.Error {
finishedHash := newFinishedHash()
finishedHash := newFinishedHash(versionTLS10)
if c.config == nil {
c.config = defaultConfig()
@ -97,11 +97,9 @@ func (c *Conn) clientHandshake() os.Error {
certs[i] = cert
}
// If we don't have a root CA set configured then anything is accepted.
// TODO(rsc): Find certificates for OS X 10.6.
if c.config.RootCAs != nil {
if !c.config.InsecureSkipVerify {
opts := x509.VerifyOptions{
Roots: c.config.RootCAs,
Roots: c.config.rootCAs(),
CurrentTime: c.config.time(),
DNSName: c.config.ServerName,
Intermediates: x509.NewCertPool(),
@ -247,11 +245,11 @@ func (c *Conn) clientHandshake() os.Error {
}
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromPreMasterSecret10(preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ )
clientHash := suite.mac(clientMAC)
c.out.prepareCipherSpec(clientCipher, clientHash)
clientHash := suite.mac(c.vers, clientMAC)
c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
if serverHello.nextProtoNeg {
@ -271,8 +269,8 @@ func (c *Conn) clientHandshake() os.Error {
c.writeRecord(recordTypeHandshake, finished.marshal())
serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ )
serverHash := suite.mac(serverMAC)
c.in.prepareCipherSpec(serverCipher, serverHash)
serverHash := suite.mac(c.vers, serverMAC)
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.readRecord(recordTypeChangeCipherSpec)
if c.err != nil {
return c.err

View file

@ -18,6 +18,7 @@ func testClientScript(t *testing.T, name string, clientScript [][]byte, config *
go func() {
cli.Write([]byte("hello\n"))
cli.Close()
c.Close()
}()
defer c.Close()

View file

@ -676,9 +676,9 @@ func (m *finishedMsg) marshal() (x []byte) {
return m.raw
}
x = make([]byte, 16)
x = make([]byte, 4+len(m.verifyData))
x[0] = typeFinished
x[3] = 12
x[3] = byte(len(m.verifyData))
copy(x[4:], m.verifyData)
m.raw = x
return
@ -686,7 +686,7 @@ func (m *finishedMsg) marshal() (x []byte) {
func (m *finishedMsg) unmarshal(data []byte) bool {
m.raw = data
if len(data) != 4+12 {
if len(data) < 4 {
return false
}
m.verifyData = data[4:]

View file

@ -14,13 +14,13 @@ import (
var tests = []interface{}{
&clientHelloMsg{},
&serverHelloMsg{},
&finishedMsg{},
&certificateMsg{},
&certificateRequestMsg{},
&certificateVerifyMsg{},
&certificateStatusMsg{},
&clientKeyExchangeMsg{},
&finishedMsg{},
&nextProtoMsg{},
}
@ -59,11 +59,12 @@ func TestMarshalUnmarshal(t *testing.T) {
break
}
if i >= 2 {
// The first two message types (ClientHello and
// ServerHello) are allowed to have parsable
// prefixes because the extension data is
// optional.
if i >= 3 {
// The first three message types (ClientHello,
// ServerHello and Finished) are allowed to
// have parsable prefixes because the extension
// data is optional and the length of the
// Finished varies across versions.
for j := 0; j < len(marshaled); j++ {
if m2.unmarshal(marshaled[0:j]) {
t.Errorf("#%d unmarshaled a prefix of length %d of %#v", i, j, m1)

View file

@ -30,7 +30,7 @@ func (c *Conn) serverHandshake() os.Error {
c.vers = vers
c.haveVers = true
finishedHash := newFinishedHash()
finishedHash := newFinishedHash(vers)
finishedHash.Write(clientHello.marshal())
hello := new(serverHelloMsg)
@ -115,7 +115,12 @@ FindCipherSuite:
}
certMsg := new(certificateMsg)
certMsg.certificates = config.Certificates[0].Certificate
if len(clientHello.serverName) > 0 {
c.serverName = clientHello.serverName
certMsg.certificates = config.getCertificateForName(clientHello.serverName).Certificate
} else {
certMsg.certificates = config.Certificates[0].Certificate
}
finishedHash.Write(certMsg.marshal())
c.writeRecord(recordTypeHandshake, certMsg.marshal())
@ -128,7 +133,6 @@ FindCipherSuite:
}
keyAgreement := suite.ka()
skx, err := keyAgreement.generateServerKeyExchange(config, clientHello, hello)
if err != nil {
c.sendAlert(alertHandshakeFailure)
@ -235,18 +239,18 @@ FindCipherSuite:
finishedHash.Write(certVerify.marshal())
}
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx)
preMasterSecret, err := keyAgreement.processClientKeyExchange(config, ckx, c.vers)
if err != nil {
c.sendAlert(alertHandshakeFailure)
return err
}
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromPreMasterSecret10(preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)
keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)
clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ )
clientHash := suite.mac(clientMAC)
c.in.prepareCipherSpec(clientCipher, clientHash)
clientHash := suite.mac(c.vers, clientMAC)
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.readRecord(recordTypeChangeCipherSpec)
if err := c.error(); err != nil {
return err
@ -283,8 +287,8 @@ FindCipherSuite:
finishedHash.Write(clientFinished.marshal())
serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ )
serverHash := suite.mac(serverMAC)
c.out.prepareCipherSpec(serverCipher, serverHash)
serverHash := suite.mac(c.vers, serverMAC)
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
finished := new(finishedMsg)

View file

@ -13,6 +13,8 @@ import (
"io"
"net"
"os"
"strconv"
"strings"
"testing"
)
@ -36,6 +38,7 @@ func init() {
testConfig.Certificates[0].Certificate = [][]byte{testCertificate}
testConfig.Certificates[0].PrivateKey = testPrivateKey
testConfig.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA}
testConfig.InsecureSkipVerify = true
}
func testClientHelloFailure(t *testing.T, m handshakeMessage, expected os.Error) {
@ -62,7 +65,7 @@ func TestSimpleError(t *testing.T) {
testClientHelloFailure(t, &serverHelloDoneMsg{}, alertUnexpectedMessage)
}
var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205, 0x0300}
var badProtocolVersions = []uint16{0x0000, 0x0005, 0x0100, 0x0105, 0x0200, 0x0205}
func TestRejectBadProtocolVersion(t *testing.T) {
for _, v := range badProtocolVersions {
@ -112,6 +115,7 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config *
go func() {
srv.Write([]byte("hello, world\n"))
srv.Close()
s.Close()
}()
defer c.Close()
@ -121,9 +125,9 @@ func testServerScript(t *testing.T, name string, serverScript [][]byte, config *
continue
}
bb := make([]byte, len(b))
_, err := io.ReadFull(c, bb)
n, err := io.ReadFull(c, bb)
if err != nil {
t.Fatalf("%s #%d: %s", name, i, err)
t.Fatalf("%s #%d: %s\nRead %d, wanted %d, got %x, wanted %x\n", name, i, err, n, len(bb), bb[:n], b)
}
if !bytes.Equal(b, bb) {
t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", name, i, bb, b)
@ -135,6 +139,13 @@ func TestHandshakeServerRC4(t *testing.T) {
testServerScript(t, "RC4", rc4ServerScript, testConfig)
}
func TestHandshakeServer3DES(t *testing.T) {
des3Config := new(Config)
*des3Config = *testConfig
des3Config.CipherSuites = []uint16{TLS_RSA_WITH_3DES_EDE_CBC_SHA}
testServerScript(t, "3DES", des3ServerScript, des3Config)
}
func TestHandshakeServerAES(t *testing.T) {
aesConfig := new(Config)
*aesConfig = *testConfig
@ -142,13 +153,30 @@ func TestHandshakeServerAES(t *testing.T) {
testServerScript(t, "AES", aesServerScript, aesConfig)
}
func TestHandshakeServerSSLv3(t *testing.T) {
testServerScript(t, "SSLv3", sslv3ServerScript, testConfig)
}
var serve = flag.Bool("serve", false, "run a TLS server on :10443")
var testCipherSuites = flag.String("ciphersuites",
"0x"+strconv.Itob(int(TLS_RSA_WITH_RC4_128_SHA), 16),
"cipher suites to accept in serving mode")
func TestRunServer(t *testing.T) {
if !*serve {
return
}
suites := strings.Split(*testCipherSuites, ",")
testConfig.CipherSuites = make([]uint16, len(suites))
for i := range suites {
suite, err := strconv.Btoui64(suites[i], 0)
if err != nil {
panic(err)
}
testConfig.CipherSuites[i] = uint16(suite)
}
l, err := Listen("tcp", ":10443", testConfig)
if err != nil {
t.Fatal(err)
@ -356,6 +384,179 @@ var rc4ServerScript = [][]byte{
},
}
var des3ServerScript = [][]byte{
{
0x16, 0x03, 0x02, 0x00, 0x7a, 0x01, 0x00, 0x00,
0x76, 0x03, 0x02, 0x4e, 0x84, 0xf4, 0x3c, 0xe4,
0xb8, 0xc7, 0xa0, 0x30, 0x55, 0x2a, 0xbc, 0xb7,
0x04, 0x6b, 0x6f, 0x87, 0x93, 0x96, 0xbd, 0x1a,
0x7a, 0x1e, 0xce, 0xd2, 0x0d, 0xf3, 0x01, 0x03,
0xbe, 0x7b, 0x17, 0x00, 0x00, 0x34, 0x00, 0x33,
0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
0x00, 0x8a, 0x01, 0x00, 0x00, 0x19, 0x00, 0x09,
0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74,
},
{
0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x16,
0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e,
0x00, 0x00, 0x00,
},
{
0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
0x82, 0x00, 0x80, 0xae, 0xcf, 0x4f, 0x70, 0x0e,
0xe5, 0xe7, 0xba, 0xef, 0x0c, 0x66, 0xe9, 0xae,
0x76, 0xf4, 0xe0, 0xbc, 0x1c, 0x22, 0x5b, 0x72,
0xc9, 0x68, 0x63, 0x44, 0xec, 0x72, 0xc2, 0xca,
0xac, 0xc2, 0xf5, 0x5c, 0x28, 0xa1, 0xaf, 0xd0,
0xc2, 0xf7, 0x79, 0x71, 0x32, 0x73, 0x86, 0xea,
0x39, 0xf6, 0x04, 0x26, 0x19, 0x84, 0x1d, 0x7d,
0xa1, 0x21, 0xa6, 0x88, 0xbf, 0x33, 0x5a, 0x64,
0xb0, 0xc2, 0xcc, 0x19, 0x7a, 0x8b, 0x6e, 0x94,
0x9e, 0x2e, 0x20, 0xbe, 0xdc, 0xe9, 0x8e, 0xae,
0x5c, 0x39, 0xc8, 0xcd, 0x0e, 0x19, 0x9a, 0xa2,
0xfc, 0x3f, 0x61, 0x9a, 0xca, 0x58, 0x69, 0x0d,
0xa8, 0x7b, 0xbe, 0x98, 0x8f, 0xb9, 0x9d, 0x8b,
0x68, 0x65, 0xa9, 0x74, 0xcc, 0x8d, 0x0c, 0xb2,
0xc4, 0x0f, 0xdc, 0x56, 0x3e, 0x44, 0x61, 0x0a,
0x26, 0x93, 0x99, 0xef, 0x67, 0xff, 0x6e, 0x73,
0x01, 0xa1, 0x90, 0x14, 0x03, 0x01, 0x00, 0x01,
0x01, 0x16, 0x03, 0x01, 0x00, 0x60, 0x49, 0x36,
0xc8, 0x38, 0x95, 0xe4, 0x5d, 0x8e, 0x80, 0x10,
0x26, 0x9f, 0x87, 0x7d, 0xcd, 0xb9, 0x32, 0x6c,
0xff, 0xaa, 0xe0, 0x07, 0xec, 0x33, 0xe2, 0x36,
0x9d, 0xd5, 0x83, 0x2c, 0xf0, 0x0a, 0xa0, 0xa8,
0x12, 0x9f, 0xca, 0x72, 0xda, 0x70, 0x7d, 0x76,
0x80, 0x12, 0x88, 0x07, 0xaa, 0x27, 0x62, 0x33,
0xab, 0x55, 0xad, 0x3c, 0x2b, 0x54, 0xc4, 0x1c,
0x91, 0xfd, 0x8f, 0x9c, 0xa7, 0x8b, 0x75, 0x10,
0xa8, 0x6e, 0xfc, 0x30, 0x52, 0x8a, 0x61, 0x02,
0xdb, 0x9c, 0x6f, 0xc8, 0x19, 0x93, 0x5d, 0x41,
0x1d, 0x36, 0x68, 0x0b, 0xec, 0x30, 0xae, 0xfb,
0x90, 0xdb, 0x6d, 0x83, 0xb0, 0xf2,
},
{
0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
0x01, 0x00, 0x28, 0x07, 0xf3, 0x33, 0x84, 0xb1,
0x5d, 0x2b, 0x52, 0xa4, 0x63, 0x3c, 0x32, 0xe0,
0x0d, 0x22, 0xf5, 0x23, 0xec, 0xf9, 0xa6, 0xec,
0xc0, 0x12, 0x69, 0x88, 0xf6, 0x7d, 0x37, 0xcd,
0xc2, 0x74, 0x2f, 0xef, 0xf6, 0x49, 0x15, 0xea,
0x88, 0x3f, 0x55, 0x17, 0x03, 0x01, 0x00, 0x28,
0xaf, 0x00, 0x84, 0xff, 0x11, 0x01, 0x6d, 0xba,
0x39, 0x5e, 0x45, 0xe1, 0x52, 0x5e, 0xc1, 0xab,
0xde, 0x5b, 0x16, 0xdd, 0xd6, 0x61, 0x57, 0xb8,
0x66, 0x8b, 0x2d, 0xde, 0x51, 0x41, 0xc5, 0x09,
0xb3, 0x6a, 0x06, 0x43, 0xb4, 0x73, 0x5c, 0xf1,
0x15, 0x03, 0x01, 0x00, 0x18, 0xbd, 0x65, 0xb2,
0xce, 0x77, 0x2e, 0xf9, 0x11, 0xc4, 0x80, 0x43,
0x5a, 0x73, 0x8b, 0x73, 0xdd, 0xf0, 0x54, 0x44,
0x7c, 0x56, 0x19, 0x54, 0xda,
},
}
var aesServerScript = [][]byte{
{
0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00,
@ -515,3 +716,165 @@ var aesServerScript = [][]byte{
0xcd, 0x84, 0xf0,
},
}
var sslv3ServerScript = [][]byte{
{
0x16, 0x03, 0x00, 0x00, 0x41, 0x01, 0x00, 0x00,
0x3d, 0x03, 0x00, 0x4e, 0x70, 0xe2, 0x18, 0x86,
0xd6, 0xc6, 0x6f, 0xf3, 0xc8, 0xf4, 0x02, 0xd6,
0x4d, 0xee, 0x17, 0x32, 0x4b, 0xd2, 0x78, 0xd8,
0xa1, 0x03, 0x5d, 0x68, 0x82, 0x89, 0xbe, 0xfd,
0x12, 0xb9, 0x06, 0x00, 0x00, 0x16, 0x00, 0x33,
0x00, 0x39, 0x00, 0x16, 0x00, 0x32, 0x00, 0x38,
0x00, 0x13, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a,
0x00, 0x05, 0x00, 0x04, 0x01, 0x00,
},
{
0x16, 0x03, 0x00, 0x00, 0x2a, 0x02, 0x00, 0x00,
0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x16,
0x03, 0x00, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74,
0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55,
0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
0xbd, 0xd9, 0x16, 0x03, 0x00, 0x00, 0x04, 0x0e,
0x00, 0x00, 0x00,
},
{
0x16, 0x03, 0x00, 0x00, 0x84, 0x10, 0x00, 0x00,
0x80, 0x74, 0x0e, 0x3a, 0xcf, 0xba, 0x9f, 0x1a,
0x9b, 0xb2, 0xa4, 0xc7, 0x5d, 0xf3, 0x0c, 0x80,
0x06, 0x80, 0xf3, 0x57, 0xb2, 0xd9, 0x36, 0x24,
0x6a, 0x06, 0x13, 0x40, 0xf9, 0x7c, 0xb9, 0x3e,
0x4b, 0x68, 0x4f, 0x21, 0x90, 0x2d, 0xbd, 0xca,
0xd4, 0x83, 0xf0, 0x7a, 0xeb, 0x7a, 0x74, 0x1b,
0xcd, 0xfe, 0x69, 0xef, 0xc0, 0x86, 0xa0, 0x24,
0x31, 0x65, 0x40, 0xd2, 0xdd, 0x6f, 0xb9, 0xd7,
0x8d, 0xc1, 0x69, 0x60, 0x44, 0x7a, 0x75, 0xfb,
0x42, 0x6a, 0x0f, 0x66, 0x45, 0x10, 0x73, 0xee,
0x87, 0x28, 0x37, 0x83, 0x86, 0xd8, 0x5a, 0xc8,
0x60, 0x87, 0xda, 0x33, 0x87, 0xaf, 0x34, 0x8b,
0xf5, 0x61, 0x63, 0x7a, 0x5c, 0x60, 0x26, 0xb9,
0xdb, 0xa1, 0xb7, 0xe3, 0x60, 0x38, 0x94, 0x5c,
0x83, 0x23, 0xd6, 0x8d, 0xc2, 0x14, 0x4a, 0x0f,
0x0e, 0x4f, 0xf9, 0x4e, 0x7b, 0x15, 0xcd, 0x18,
0x04, 0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16,
0x03, 0x00, 0x00, 0x3c, 0xbd, 0xbc, 0xec, 0xdc,
0x79, 0xb1, 0xae, 0x16, 0xc9, 0x26, 0x9a, 0xc0,
0xc0, 0x2c, 0x33, 0x36, 0x13, 0x91, 0x58, 0x5d,
0x7d, 0xee, 0x4e, 0xd8, 0x7e, 0xac, 0x88, 0x87,
0x0a, 0x75, 0x66, 0xb1, 0x44, 0x79, 0x2f, 0x42,
0xe8, 0x92, 0x74, 0x4c, 0xab, 0x36, 0xc8, 0x17,
0x5f, 0x02, 0x8a, 0x20, 0x53, 0xe9, 0x1d, 0xb4,
0xfe, 0x5c, 0x2b, 0xd9, 0x0a, 0xfb, 0xc6, 0x63,
},
{
0x14, 0x03, 0x00, 0x00, 0x01, 0x01, 0x16, 0x03,
0x00, 0x00, 0x3c, 0xaa, 0xa1, 0x98, 0xc4, 0x6b,
0x5a, 0x16, 0x3f, 0x5f, 0xa4, 0x96, 0x3e, 0x78,
0xe4, 0x6f, 0x49, 0x05, 0x47, 0xc4, 0x05, 0x60,
0xeb, 0x0b, 0x45, 0xe3, 0xbc, 0x50, 0x11, 0x24,
0x5f, 0x01, 0xd7, 0xb8, 0x8f, 0x60, 0x63, 0x66,
0xbd, 0x3e, 0xd9, 0xa8, 0x80, 0x43, 0x9f, 0x0b,
0x51, 0x61, 0xed, 0x13, 0xc6, 0x21, 0xd0, 0xfe,
0xbc, 0x17, 0x3c, 0x36, 0xb0, 0x82, 0x7f, 0x17,
0x03, 0x00, 0x00, 0x21, 0xee, 0x44, 0xf3, 0xa6,
0x88, 0x9d, 0x78, 0x44, 0xde, 0xdf, 0xeb, 0xc5,
0xad, 0xc4, 0xcc, 0x56, 0x5c, 0x54, 0x96, 0x52,
0x3f, 0xd9, 0x40, 0x6e, 0x79, 0xd8, 0x58, 0x78,
0x4f, 0x5a, 0xe9, 0x06, 0xef, 0x15, 0x03, 0x00,
0x00, 0x16, 0xd3, 0xc2, 0x52, 0x99, 0x2a, 0x84,
0xc4, 0x52, 0x5f, 0x3b, 0x19, 0xe7, 0xfc, 0x65,
0xaf, 0xd3, 0xb7, 0xa3, 0xcc, 0x4a, 0x1d, 0x2e,
},
}

View file

@ -24,7 +24,7 @@ func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, clientHello
return nil, nil
}
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) {
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, os.Error) {
preMasterSecret := make([]byte, 48)
_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
if err != nil {
@ -34,11 +34,15 @@ func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKe
if len(ckx.ciphertext) < 2 {
return nil, os.NewError("bad ClientKeyExchange")
}
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 {
return nil, os.NewError("bad ClientKeyExchange")
ciphertext := ckx.ciphertext
if version != versionSSL30 {
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 {
return nil, os.NewError("bad ClientKeyExchange")
}
ciphertext = ckx.ciphertext[2:]
}
ciphertext := ckx.ciphertext[2:]
err = rsa.DecryptPKCS1v15SessionKey(config.rand(), config.Certificates[0].PrivateKey, ciphertext, preMasterSecret)
if err != nil {
@ -159,7 +163,7 @@ Curve:
return skx, nil
}
func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg) ([]byte, os.Error) {
func (ka *ecdheRSAKeyAgreement) processClientKeyExchange(config *Config, ckx *clientKeyExchangeMsg, version uint16) ([]byte, os.Error) {
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, os.NewError("bad ClientKeyExchange")
}

View file

@ -63,6 +63,39 @@ func pRF10(result, secret, label, seed []byte) {
}
}
// pRF30 implements the SSL 3.0 pseudo-random function, as defined in
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6.
func pRF30(result, secret, label, seed []byte) {
hashSHA1 := sha1.New()
hashMD5 := md5.New()
done := 0
i := 0
// RFC5246 section 6.3 says that the largest PRF output needed is 128
// bytes. Since no more ciphersuites will be added to SSLv3, this will
// remain true. Each iteration gives us 16 bytes so 10 iterations will
// be sufficient.
var b [11]byte
for done < len(result) {
for j := 0; j <= i; j++ {
b[j] = 'A' + byte(i)
}
hashSHA1.Reset()
hashSHA1.Write(b[:i+1])
hashSHA1.Write(secret)
hashSHA1.Write(seed)
digest := hashSHA1.Sum()
hashMD5.Reset()
hashMD5.Write(secret)
hashMD5.Write(digest)
done += copy(result[done:], hashMD5.Sum())
i++
}
}
const (
tlsRandomLength = 32 // Length of a random nonce in TLS 1.1.
masterSecretLength = 48 // Length of a master secret in TLS 1.1.
@ -77,19 +110,24 @@ var serverFinishedLabel = []byte("server finished")
// keysFromPreMasterSecret generates the connection keys from the pre master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, section 6.3.
func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
func keysFromPreMasterSecret(version uint16, preMasterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
prf := pRF10
if version == versionSSL30 {
prf = pRF30
}
var seed [tlsRandomLength * 2]byte
copy(seed[0:len(clientRandom)], clientRandom)
copy(seed[len(clientRandom):], serverRandom)
masterSecret = make([]byte, masterSecretLength)
pRF10(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
prf(masterSecret, preMasterSecret, masterSecretLabel, seed[0:])
copy(seed[0:len(clientRandom)], serverRandom)
copy(seed[len(serverRandom):], clientRandom)
n := 2*macLen + 2*keyLen + 2*ivLen
keyMaterial := make([]byte, n)
pRF10(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
prf(keyMaterial, masterSecret, keyExpansionLabel, seed[0:])
clientMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:]
serverMAC = keyMaterial[:macLen]
@ -104,6 +142,10 @@ func keysFromPreMasterSecret10(preMasterSecret, clientRandom, serverRandom []byt
return
}
func newFinishedHash(version uint16) finishedHash {
return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New(), version}
}
// A finishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message.
type finishedHash struct {
@ -111,10 +153,7 @@ type finishedHash struct {
clientSHA1 hash.Hash
serverMD5 hash.Hash
serverSHA1 hash.Hash
}
func newFinishedHash() finishedHash {
return finishedHash{md5.New(), sha1.New(), md5.New(), sha1.New()}
version uint16
}
func (h finishedHash) Write(msg []byte) (n int, err os.Error) {
@ -125,9 +164,10 @@ func (h finishedHash) Write(msg []byte) (n int, err os.Error) {
return len(msg), nil
}
// finishedSum calculates the contents of the verify_data member of a Finished
// message given the MD5 and SHA1 hashes of a set of handshake messages.
func finishedSum(md5, sha1, label, masterSecret []byte) []byte {
// finishedSum10 calculates the contents of the verify_data member of a TLSv1
// Finished message given the MD5 and SHA1 hashes of a set of handshake
// messages.
func finishedSum10(md5, sha1, label, masterSecret []byte) []byte {
seed := make([]byte, len(md5)+len(sha1))
copy(seed, md5)
copy(seed[len(md5):], sha1)
@ -136,18 +176,61 @@ func finishedSum(md5, sha1, label, masterSecret []byte) []byte {
return out
}
// finishedSum30 calculates the contents of the verify_data member of a SSLv3
// Finished message given the MD5 and SHA1 hashes of a set of handshake
// messages.
func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic [4]byte) []byte {
md5.Write(magic[:])
md5.Write(masterSecret)
md5.Write(ssl30Pad1[:])
md5Digest := md5.Sum()
md5.Reset()
md5.Write(masterSecret)
md5.Write(ssl30Pad2[:])
md5.Write(md5Digest)
md5Digest = md5.Sum()
sha1.Write(magic[:])
sha1.Write(masterSecret)
sha1.Write(ssl30Pad1[:40])
sha1Digest := sha1.Sum()
sha1.Reset()
sha1.Write(masterSecret)
sha1.Write(ssl30Pad2[:40])
sha1.Write(sha1Digest)
sha1Digest = sha1.Sum()
ret := make([]byte, len(md5Digest)+len(sha1Digest))
copy(ret, md5Digest)
copy(ret[len(md5Digest):], sha1Digest)
return ret
}
var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54}
var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52}
// clientSum returns the contents of the verify_data member of a client's
// Finished message.
func (h finishedHash) clientSum(masterSecret []byte) []byte {
if h.version == versionSSL30 {
return finishedSum30(h.clientMD5, h.clientSHA1, masterSecret, ssl3ClientFinishedMagic)
}
md5 := h.clientMD5.Sum()
sha1 := h.clientSHA1.Sum()
return finishedSum(md5, sha1, clientFinishedLabel, masterSecret)
return finishedSum10(md5, sha1, clientFinishedLabel, masterSecret)
}
// serverSum returns the contents of the verify_data member of a server's
// Finished message.
func (h finishedHash) serverSum(masterSecret []byte) []byte {
if h.version == versionSSL30 {
return finishedSum30(h.serverMD5, h.serverSHA1, masterSecret, ssl3ServerFinishedMagic)
}
md5 := h.serverMD5.Sum()
sha1 := h.serverSHA1.Sum()
return finishedSum(md5, sha1, serverFinishedLabel, masterSecret)
return finishedSum10(md5, sha1, serverFinishedLabel, masterSecret)
}

View file

@ -34,6 +34,7 @@ func TestSplitPreMasterSecret(t *testing.T) {
}
type testKeysFromTest struct {
version uint16
preMasterSecret string
clientRandom, serverRandom string
masterSecret string
@ -47,7 +48,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
in, _ := hex.DecodeString(test.preMasterSecret)
clientRandom, _ := hex.DecodeString(test.clientRandom)
serverRandom, _ := hex.DecodeString(test.serverRandom)
master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret10(in, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
master, clientMAC, serverMAC, clientKey, serverKey, _, _ := keysFromPreMasterSecret(test.version, in, clientRandom, serverRandom, test.macLen, test.keyLen, 0)
masterString := hex.EncodeToString(master)
clientMACString := hex.EncodeToString(clientMAC)
serverMACString := hex.EncodeToString(serverMAC)
@ -58,7 +59,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
serverMACString != test.serverMAC ||
clientKeyString != test.clientKey ||
serverKeyString != test.serverKey {
t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverMACString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey)
t.Errorf("#%d: got: (%s, %s, %s, %s, %s) want: (%s, %s, %s, %s, %s)", i, masterString, clientMACString, serverMACString, clientKeyString, serverKeyString, test.masterSecret, test.clientMAC, test.serverMAC, test.clientKey, test.serverKey)
}
}
}
@ -66,6 +67,7 @@ func TestKeysFromPreMasterSecret(t *testing.T) {
// These test vectors were generated from GnuTLS using `gnutls-cli --insecure -d 9 `
var testKeysFromTests = []testKeysFromTest{
{
versionTLS10,
"0302cac83ad4b1db3b9ab49ad05957de2a504a634a386fc600889321e1a971f57479466830ac3e6f468e87f5385fa0c5",
"4ae66303755184a3917fcb44880605fcc53baa01912b22ed94473fc69cebd558",
"4ae663020ec16e6bb5130be918cfcafd4d765979a3136a5d50c593446e4e44db",
@ -78,6 +80,7 @@ var testKeysFromTests = []testKeysFromTest{
16,
},
{
versionTLS10,
"03023f7527316bc12cbcd69e4b9e8275d62c028f27e65c745cfcddc7ce01bd3570a111378b63848127f1c36e5f9e4890",
"4ae66364b5ea56b20ce4e25555aed2d7e67f42788dd03f3fee4adae0459ab106",
"4ae66363ab815cbf6a248b87d6b556184e945e9b97fbdf247858b0bdafacfa1c",
@ -90,6 +93,7 @@ var testKeysFromTests = []testKeysFromTest{
16,
},
{
versionTLS10,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
@ -101,4 +105,17 @@ var testKeysFromTests = []testKeysFromTest{
20,
16,
},
{
versionSSL30,
"832d515f1d61eebb2be56ba0ef79879efb9b527504abb386fb4310ed5d0e3b1f220d3bb6b455033a2773e6d8bdf951d278a187482b400d45deb88a5d5a6bb7d6a7a1decc04eb9ef0642876cd4a82d374d3b6ff35f0351dc5d411104de431375355addc39bfb1f6329fb163b0bc298d658338930d07d313cd980a7e3d9196cac1",
"4ae663b2ee389c0de147c509d8f18f5052afc4aaf9699efe8cb05ece883d3a5e",
"4ae664d503fd4cff50cfc1fb8fc606580f87b0fcdac9554ba0e01d785bdf278e",
"a614863e56299dcffeea2938f22c2ba023768dbe4b3f6877bc9c346c6ae529b51d9cb87ff9695ea4d01f2205584405b2",
"2c450d5b6f6e2013ac6bea6a0b32200d4e1ffb94",
"7a7a7438769536f2fb1ae49a61f0703b79b2dc53",
"f8f6b26c10f12855c9aafb1e0e839ccf",
"2b9d4b4a60cb7f396780ebff50650419",
20,
16,
},
}

View file

@ -0,0 +1,95 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
/*
// Note: We disable -Werror here because the code in this file uses a deprecated API to stay
// compatible with both Mac OS X 10.6 and 10.7. Using a deprecated function on Darwin generates
// a warning.
#cgo CFLAGS: -Wno-error
#cgo LDFLAGS: -framework CoreFoundation -framework Security
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
// FetchPEMRoots fetches the system's list of trusted X.509 root certificates.
//
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
// certificates of the system. On failure, the function returns -1.
//
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
// we've consumed its content.
int FetchPEMRoots(CFDataRef *pemRoots) {
if (pemRoots == NULL) {
return -1;
}
CFArrayRef certs = NULL;
OSStatus err = SecTrustCopyAnchorCertificates(&certs);
if (err != noErr) {
return -1;
}
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
int i, ncerts = CFArrayGetCount(certs);
for (i = 0; i < ncerts; i++) {
CFDataRef data = NULL;
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
if (cert == NULL) {
continue;
}
// SecKeychainImportExport is deprecated in >= OS X 10.7, and has been replaced by
// SecItemExport. If we're built on a host with a Lion SDK, this code gets conditionally
// included in the output, also for binaries meant for 10.6.
//
// To make sure that we run on both Mac OS X 10.6 and 10.7 we use weak linking
// and check whether SecItemExport is available before we attempt to call it. On
// 10.6, this won't be the case, and we'll fall back to calling SecKeychainItemExport.
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
if (SecItemExport) {
err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
if (err != noErr) {
continue;
}
} else
#endif
if (data == NULL) {
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
if (err != noErr) {
continue;
}
}
if (data != NULL) {
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
CFRelease(data);
}
}
CFRelease(certs);
*pemRoots = combinedData;
return 0;
}
*/
import "C"
import (
"crypto/x509"
"unsafe"
)
func initDefaultRoots() {
roots := x509.NewCertPool()
var data C.CFDataRef = nil
err := C.FetchPEMRoots(&data)
if err != -1 {
defer C.CFRelease(C.CFTypeRef(data))
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
roots.AppendCertsFromPEM(buf)
}
varDefaultRoots = roots
}

View file

@ -0,0 +1,8 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
func initDefaultRoots() {
}

View file

@ -0,0 +1,36 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"testing"
)
var tlsServers = []string{
"google.com:443",
"github.com:443",
"twitter.com:443",
}
func TestOSCertBundles(t *testing.T) {
defaultRoots()
if testing.Short() {
t.Logf("skipping certificate tests in short mode")
return
}
for _, addr := range tlsServers {
conn, err := Dial("tcp", addr, nil)
if err != nil {
t.Errorf("unable to verify %v: %v", addr, err)
continue
}
err = conn.Close()
if err != nil {
t.Error(err)
}
}
}

View file

@ -0,0 +1,29 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"crypto/x509"
"io/ioutil"
)
// Possible certificate files; stop after finding one.
var certFiles = []string{
"/etc/ssl/certs/ca-certificates.crt", // Linux etc
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL
"/etc/ssl/ca-bundle.pem", // OpenSUSE
}
func initDefaultRoots() {
roots := x509.NewCertPool()
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
break
}
}
varDefaultRoots = roots
}

View file

@ -0,0 +1,54 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tls
import (
"crypto/x509"
"reflect"
"syscall"
"unsafe"
)
func loadStore(roots *x509.CertPool, name string) {
store, errno := syscall.CertOpenSystemStore(syscall.InvalidHandle, syscall.StringToUTF16Ptr(name))
if errno != 0 {
return
}
var cert *syscall.CertContext
for {
cert = syscall.CertEnumCertificatesInStore(store, cert)
if cert == nil {
break
}
var asn1Slice []byte
hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&asn1Slice))
hdrp.Data = cert.EncodedCert
hdrp.Len = int(cert.Length)
hdrp.Cap = int(cert.Length)
buf := make([]byte, len(asn1Slice))
copy(buf, asn1Slice)
if cert, err := x509.ParseCertificate(buf); err == nil {
roots.AddCert(cert)
}
}
syscall.CertCloseStore(store, 0)
}
func initDefaultRoots() {
roots := x509.NewCertPool()
// Roots
loadStore(roots, "ROOT")
// Intermediates
loadStore(roots, "CA")
varDefaultRoots = roots
}

View file

@ -5,9 +5,7 @@
package x509
import (
"crypto/x509/pkix"
"encoding/pem"
"strings"
)
// Roots is a set of certificates.
@ -26,10 +24,6 @@ func NewCertPool() *CertPool {
}
}
func nameToKey(name *pkix.Name) string {
return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
}
// findVerifiedParents attempts to find certificates in s which have signed the
// given certificate. If no such certificate can be found or the signature
// doesn't match, it returns nil.
@ -40,7 +34,7 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int) {
candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
}
if len(candidates) == 0 {
candidates = s.byName[nameToKey(&cert.Issuer)]
candidates = s.byName[string(cert.RawIssuer)]
}
for _, c := range candidates {
@ -72,7 +66,7 @@ func (s *CertPool) AddCert(cert *Certificate) {
keyId := string(cert.SubjectKeyId)
s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
}
name := nameToKey(&cert.Subject)
name := string(cert.RawSubject)
s.byName[name] = append(s.byName[name], n)
}

View file

@ -0,0 +1,122 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
"asn1"
"big"
"os"
"crypto/rsa"
)
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
type pkcs1PrivateKey struct {
Version int
N *big.Int
E int
D *big.Int
P *big.Int
Q *big.Int
// We ignore these values, if present, because rsa will calculate them.
Dp *big.Int `asn1:"optional"`
Dq *big.Int `asn1:"optional"`
Qinv *big.Int `asn1:"optional"`
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
}
type pkcs1AdditionalRSAPrime struct {
Prime *big.Int
// We ignore these values because rsa will calculate them.
Exp *big.Int
Coeff *big.Int
}
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
var priv pkcs1PrivateKey
rest, err := asn1.Unmarshal(der, &priv)
if len(rest) > 0 {
err = asn1.SyntaxError{"trailing data"}
return
}
if err != nil {
return
}
if priv.Version > 1 {
return nil, os.NewError("x509: unsupported private key version")
}
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
return nil, os.NewError("private key contains zero or negative value")
}
key = new(rsa.PrivateKey)
key.PublicKey = rsa.PublicKey{
E: priv.E,
N: priv.N,
}
key.D = priv.D
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
key.Primes[0] = priv.P
key.Primes[1] = priv.Q
for i, a := range priv.AdditionalPrimes {
if a.Prime.Sign() <= 0 {
return nil, os.NewError("private key contains zero or negative prime")
}
key.Primes[i+2] = a.Prime
// We ignore the other two values because rsa will calculate
// them as needed.
}
err = key.Validate()
if err != nil {
return nil, err
}
key.Precompute()
return
}
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
key.Precompute()
version := 0
if len(key.Primes) > 2 {
version = 1
}
priv := pkcs1PrivateKey{
Version: version,
N: key.N,
E: key.PublicKey.E,
D: key.D,
P: key.Primes[0],
Q: key.Primes[1],
Dp: key.Precomputed.Dp,
Dq: key.Precomputed.Dq,
Qinv: key.Precomputed.Qinv,
}
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
for i, values := range key.Precomputed.CRTValues {
priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
priv.AdditionalPrimes[i].Exp = values.Exp
priv.AdditionalPrimes[i].Coeff = values.Coeff
}
b, _ := asn1.Marshal(priv)
return b
}
// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key.
type rsaPublicKey struct {
N *big.Int
E int
}

View file

@ -43,6 +43,8 @@ type Name struct {
Locality, Province []string
StreetAddress, PostalCode []string
SerialNumber, CommonName string
Names []AttributeTypeAndValue
}
func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
@ -51,6 +53,7 @@ func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
continue
}
atv := rdn[0]
n.Names = append(n.Names, atv)
value, ok := atv.Value.(string)
if !ok {
continue

View file

@ -5,6 +5,7 @@
package x509
import (
"crypto/x509/pkix"
"encoding/pem"
"os"
"strings"
@ -31,7 +32,7 @@ var verifyTests = []verifyTest{
dnsName: "www.google.com",
expectedChains: [][]string{
[]string{"Google", "Thawte", "VeriSign"},
{"Google", "Thawte", "VeriSign"},
},
},
{
@ -68,7 +69,7 @@ var verifyTests = []verifyTest{
dnsName: "www.google.com",
expectedChains: [][]string{
[]string{"Google", "Thawte", "VeriSign"},
{"Google", "Thawte", "VeriSign"},
},
},
{
@ -78,7 +79,7 @@ var verifyTests = []verifyTest{
currentTime: 1302726541,
expectedChains: [][]string{
[]string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
},
},
{
@ -88,8 +89,8 @@ var verifyTests = []verifyTest{
currentTime: 1302726541,
expectedChains: [][]string{
[]string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
[]string{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority"},
{"dnssec-exp", "StartCom Class 1", "StartCom Certification Authority", "StartCom Certification Authority"},
},
},
}
@ -211,6 +212,10 @@ func chainToDebugString(chain []*Certificate) string {
return chainStr
}
func nameToKey(name *pkix.Name) string {
return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
}
const verisignRoot = `-----BEGIN CERTIFICATE-----
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz

View file

@ -20,108 +20,59 @@ import (
"time"
)
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
type pkcs1PrivateKey struct {
Version int
N *big.Int
E int
D *big.Int
P *big.Int
Q *big.Int
// We ignore these values, if present, because rsa will calculate them.
Dp *big.Int `asn1:"optional"`
Dq *big.Int `asn1:"optional"`
Qinv *big.Int `asn1:"optional"`
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional"`
// pkixPublicKey reflects a PKIX public key structure. See SubjectPublicKeyInfo
// in RFC 3280.
type pkixPublicKey struct {
Algo pkix.AlgorithmIdentifier
BitString asn1.BitString
}
type pkcs1AdditionalRSAPrime struct {
Prime *big.Int
// We ignore these values because rsa will calculate them.
Exp *big.Int
Coeff *big.Int
}
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
var priv pkcs1PrivateKey
rest, err := asn1.Unmarshal(der, &priv)
if len(rest) > 0 {
err = asn1.SyntaxError{"trailing data"}
// ParsePKIXPublicKey parses a DER encoded public key. These values are
// typically found in PEM blocks with "BEGIN PUBLIC KEY".
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err os.Error) {
var pki publicKeyInfo
if _, err = asn1.Unmarshal(derBytes, &pki); err != nil {
return
}
if err != nil {
return
algo := getPublicKeyAlgorithmFromOID(pki.Algorithm.Algorithm)
if algo == UnknownPublicKeyAlgorithm {
return nil, os.NewError("ParsePKIXPublicKey: unknown public key algorithm")
}
if priv.Version > 1 {
return nil, os.NewError("x509: unsupported private key version")
}
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
return nil, os.NewError("private key contains zero or negative value")
}
key = new(rsa.PrivateKey)
key.PublicKey = rsa.PublicKey{
E: priv.E,
N: priv.N,
}
key.D = priv.D
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
key.Primes[0] = priv.P
key.Primes[1] = priv.Q
for i, a := range priv.AdditionalPrimes {
if a.Prime.Sign() <= 0 {
return nil, os.NewError("private key contains zero or negative prime")
}
key.Primes[i+2] = a.Prime
// We ignore the other two values because rsa will calculate
// them as needed.
}
err = key.Validate()
if err != nil {
return nil, err
}
key.Precompute()
return
return parsePublicKey(algo, &pki)
}
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
key.Precompute()
// MarshalPKIXPublicKey serialises a public key to DER-encoded PKIX format.
func MarshalPKIXPublicKey(pub interface{}) ([]byte, os.Error) {
var pubBytes []byte
version := 0
if len(key.Primes) > 2 {
version = 1
switch pub := pub.(type) {
case *rsa.PublicKey:
pubBytes, _ = asn1.Marshal(rsaPublicKey{
N: pub.N,
E: pub.E,
})
default:
return nil, os.NewError("MarshalPKIXPublicKey: unknown public key type")
}
priv := pkcs1PrivateKey{
Version: version,
N: key.N,
E: key.PublicKey.E,
D: key.D,
P: key.Primes[0],
Q: key.Primes[1],
Dp: key.Precomputed.Dp,
Dq: key.Precomputed.Dq,
Qinv: key.Precomputed.Qinv,
pkix := pkixPublicKey{
Algo: pkix.AlgorithmIdentifier{
Algorithm: []int{1, 2, 840, 113549, 1, 1, 1},
// This is a NULL parameters value which is technically
// superfluous, but most other code includes it and, by
// doing this, we match their public key hashes.
Parameters: asn1.RawValue{
Tag: 5,
},
},
BitString: asn1.BitString{
Bytes: pubBytes,
BitLength: 8 * len(pubBytes),
},
}
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
for i, values := range key.Precomputed.CRTValues {
priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
priv.AdditionalPrimes[i].Exp = values.Exp
priv.AdditionalPrimes[i].Coeff = values.Coeff
}
b, _ := asn1.Marshal(priv)
return b
ret, _ := asn1.Marshal(pkix)
return ret, nil
}
// These structures reflect the ASN.1 structure of X.509 certificates.:
@ -138,9 +89,9 @@ type tbsCertificate struct {
Version int `asn1:"optional,explicit,default:1,tag:0"`
SerialNumber *big.Int
SignatureAlgorithm pkix.AlgorithmIdentifier
Issuer pkix.RDNSequence
Issuer asn1.RawValue
Validity validity
Subject pkix.RDNSequence
Subject asn1.RawValue
PublicKey publicKeyInfo
UniqueId asn1.BitString `asn1:"optional,tag:1"`
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
@ -339,6 +290,8 @@ type Certificate struct {
Raw []byte // Complete ASN.1 DER content (certificate, signature algorithm and signature).
RawTBSCertificate []byte // Certificate part of raw ASN.1 DER content.
RawSubjectPublicKeyInfo []byte // DER encoded SubjectPublicKeyInfo.
RawSubject []byte // DER encoded Subject
RawIssuer []byte // DER encoded Issuer
Signature []byte
SignatureAlgorithm SignatureAlgorithm
@ -483,11 +436,6 @@ type basicConstraints struct {
MaxPathLen int `asn1:"optional"`
}
type rsaPublicKey struct {
N *big.Int
E int
}
// RFC 5280 4.2.1.4
type policyInformation struct {
Policy asn1.ObjectIdentifier
@ -556,6 +504,8 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {
out.Raw = in.Raw
out.RawTBSCertificate = in.TBSCertificate.Raw
out.RawSubjectPublicKeyInfo = in.TBSCertificate.PublicKey.Raw
out.RawSubject = in.TBSCertificate.Subject.FullBytes
out.RawIssuer = in.TBSCertificate.Issuer.FullBytes
out.Signature = in.SignatureValue.RightAlign()
out.SignatureAlgorithm =
@ -575,8 +525,18 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) {
out.Version = in.TBSCertificate.Version + 1
out.SerialNumber = in.TBSCertificate.SerialNumber
out.Issuer.FillFromRDNSequence(&in.TBSCertificate.Issuer)
out.Subject.FillFromRDNSequence(&in.TBSCertificate.Subject)
var issuer, subject pkix.RDNSequence
if _, err := asn1.Unmarshal(in.TBSCertificate.Subject.FullBytes, &subject); err != nil {
return nil, err
}
if _, err := asn1.Unmarshal(in.TBSCertificate.Issuer.FullBytes, &issuer); err != nil {
return nil, err
}
out.Issuer.FillFromRDNSequence(&issuer)
out.Subject.FillFromRDNSequence(&subject)
out.NotBefore = in.TBSCertificate.Validity.NotBefore
out.NotAfter = in.TBSCertificate.Validity.NotAfter
@ -968,14 +928,23 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P
return
}
asn1Issuer, err := asn1.Marshal(parent.Issuer.ToRDNSequence())
if err != nil {
return
}
asn1Subject, err := asn1.Marshal(parent.Subject.ToRDNSequence())
if err != nil {
return
}
encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey}
c := tbsCertificate{
Version: 2,
SerialNumber: template.SerialNumber,
SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA},
Issuer: parent.Subject.ToRDNSequence(),
Issuer: asn1.RawValue{FullBytes: asn1Issuer},
Validity: validity{template.NotBefore, template.NotAfter},
Subject: template.Subject.ToRDNSequence(),
Subject: asn1.RawValue{FullBytes: asn1Subject},
PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey},
Extensions: extensions,
}

View file

@ -6,6 +6,7 @@ package x509
import (
"asn1"
"bytes"
"big"
"crypto/dsa"
"crypto/rand"
@ -34,6 +35,40 @@ func TestParsePKCS1PrivateKey(t *testing.T) {
}
}
func TestParsePKIXPublicKey(t *testing.T) {
block, _ := pem.Decode([]byte(pemPublicKey))
pub, err := ParsePKIXPublicKey(block.Bytes)
if err != nil {
t.Errorf("Failed to parse RSA public key: %s", err)
return
}
rsaPub, ok := pub.(*rsa.PublicKey)
if !ok {
t.Errorf("Value returned from ParsePKIXPublicKey was not an RSA public key")
return
}
pubBytes2, err := MarshalPKIXPublicKey(rsaPub)
if err != nil {
t.Errorf("Failed to marshal RSA public key for the second time: %s", err)
return
}
if !bytes.Equal(pubBytes2, block.Bytes) {
t.Errorf("Reserialization of public key didn't match. got %x, want %x", pubBytes2, block.Bytes)
}
}
var pemPublicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3VoPN9PKUjKFLMwOge6+
wnDi8sbETGIx2FKXGgqtAKpzmem53kRGEQg8WeqRmp12wgp74TGpkEXsGae7RS1k
enJCnma4fii+noGH7R0qKgHvPrI2Bwa9hzsH8tHxpyM3qrXslOmD45EH9SxIDUBJ
FehNdaPbLP1gFyahKMsdfxFJLUvbUycuZSJ2ZnIgeVxwm4qbSvZInL9Iu4FzuPtg
fINKcbbovy1qq4KvPIrXzhbY3PWDc6btxCf3SE0JdE1MCPThntB62/bLMSQ7xdDR
FF53oIpvxe/SCOymfWq/LW849Ytv3Xwod0+wzAP8STXG4HSELS4UedPYeHJJJYcZ
+QIDAQAB
-----END PUBLIC KEY-----
`
var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu

View file

@ -267,7 +267,7 @@ func (r *Reader) parseField() (haveField bool, delim int, err os.Error) {
}
if r.TrimLeadingSpace {
for unicode.IsSpace(rune) {
for rune != '\n' && unicode.IsSpace(rune) {
rune, err = r.readRune()
if err != nil {
return false, 0, err
@ -355,7 +355,7 @@ func (r *Reader) parseField() (haveField bool, delim int, err os.Error) {
c := r.column
rune, err = r.readRune()
if r.TrimLeadingSpace {
for unicode.IsSpace(rune) {
for rune != '\n' && unicode.IsSpace(rune) {
rune, err = r.readRune()
if err != nil {
break

View file

@ -127,10 +127,9 @@ field"`,
Output: [][]string{{`a""b`, `c`}},
},
{
Name: "BadDoubleQuotes",
Input: `a""b,c`,
Output: [][]string{{`a""b`, `c`}},
Error: `bare " in non-quoted-field`, Line: 1, Column: 1,
Name: "BadDoubleQuotes",
Input: `a""b,c`,
Error: `bare " in non-quoted-field`, Line: 1, Column: 1,
},
{
Name: "TrimQuote",
@ -231,6 +230,23 @@ x,,,
{"", "", "", ""},
},
},
{
Name: "Issue 2366",
TrailingComma: true,
TrimLeadingSpace: true,
Input: "a,b,\nc,d,e",
Output: [][]string{
{"a", "b", ""},
{"c", "d", "e"},
},
},
{
Name: "Issue 2366a",
TrailingComma: false,
TrimLeadingSpace: true,
Input: "a,b,\nc,d,e",
Error: "extra delimiter at end of line",
},
}
func TestRead(t *testing.T) {

View file

@ -579,6 +579,6 @@ Error:
// If the parse fails, take the type out of the cache
// so that the next call with this offset doesn't hit
// the cache and return success.
d.typeCache[off] = nil, false
delete(d.typeCache, off)
return nil, err
}

View file

@ -17,9 +17,9 @@ import (
// A ByteOrder specifies how to convert byte sequences into
// 16-, 32-, or 64-bit unsigned integers.
type ByteOrder interface {
Uint16(b []byte) uint16
Uint32(b []byte) uint32
Uint64(b []byte) uint64
Uint16([]byte) uint16
Uint32([]byte) uint32
Uint64([]byte) uint64
PutUint16([]byte, uint16)
PutUint32([]byte, uint32)
PutUint64([]byte, uint64)

View file

@ -0,0 +1,149 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package binary
// This file implements "varint" encoding of 64-bit integers.
// The encoding is:
// - unsigned integers are serialized 7 bits at a time, starting with the
// least significant bits
// - the most significant bit (msb) in each output byte indicates if there
// is a continuation byte (msb = 1)
// - signed integers are mapped to unsigned integers using "zig-zag"
// encoding: Positive values x are written as 2*x + 0, negative values
// are written as 2*(^x) + 1; that is, negative numbers are complemented
// and whether to complement is encoded in bit 0.
//
// Design note:
// At most 10 bytes are needed for 64-bit values. The encoding could
// be more dense: a full 64-bit value needs an extra byte just to hold bit 63.
// Instead, the msb of the previous byte could be used to hold bit 63 since we
// know there can't be more than 64 bits. This is a trivial improvement and
// would reduce the maximum encoding length to 9 bytes. However, it breaks the
// invariant that the msb is always the "continuation bit" and thus makes the
// format incompatible with a varint encoding for larger numbers (say 128-bit).
import (
"io"
"os"
)
// MaxVarintLenN is the maximum length of a varint-encoded N-bit integer.
const (
MaxVarintLen16 = 3
MaxVarintLen32 = 5
MaxVarintLen64 = 10
)
// PutUvarint encodes a uint64 into buf and returns the number of bytes written.
func PutUvarint(buf []byte, x uint64) int {
i := 0
for x >= 0x80 {
buf[i] = byte(x) | 0x80
x >>= 7
i++
}
buf[i] = byte(x)
return i + 1
}
// Uvarint decodes a uint64 from buf and returns that value and the
// number of bytes read (> 0). If an error occurred, the value is 0
// and the number of bytes n is <= 0 meaning:
//
// n == 0: buf too small
// n < 0: value larger than 64 bits (overflow)
// and -n is the number of bytes read
//
func Uvarint(buf []byte) (uint64, int) {
var x uint64
var s uint
for i, b := range buf {
if b < 0x80 {
if i > 9 || i == 9 && b > 1 {
return 0, -(i + 1) // overflow
}
return x | uint64(b)<<s, i + 1
}
x |= uint64(b&0x7f) << s
s += 7
}
return 0, 0
}
// PutVarint encodes an int64 into buf and returns the number of bytes written.
func PutVarint(buf []byte, x int64) int {
ux := uint64(x) << 1
if x < 0 {
ux = ^ux
}
return PutUvarint(buf, ux)
}
// Varint decodes an int64 from buf and returns that value and the
// number of bytes read (> 0). If an error occurred, the value is 0
// and the number of bytes n is <= 0 with the following meaning:
//
// n == 0: buf too small
// n < 0: value larger than 64 bits (overflow)
// and -n is the number of bytes read
//
func Varint(buf []byte) (int64, int) {
ux, n := Uvarint(buf) // ok to continue in presence of error
x := int64(ux >> 1)
if ux&1 != 0 {
x = ^x
}
return x, n
}
// WriteUvarint encodes x and writes the result to w.
func WriteUvarint(w io.Writer, x uint64) os.Error {
var buf [MaxVarintLen64]byte
n := PutUvarint(buf[:], x)
_, err := w.Write(buf[0:n])
return err
}
var overflow = os.NewError("binary: varint overflows a 64-bit integer")
// ReadUvarint reads an encoded unsigned integer from r and returns it as a uint64.
func ReadUvarint(r io.ByteReader) (uint64, os.Error) {
var x uint64
var s uint
for i := 0; ; i++ {
b, err := r.ReadByte()
if err != nil {
return x, err
}
if b < 0x80 {
if i > 9 || i == 9 && b > 1 {
return x, overflow
}
return x | uint64(b)<<s, nil
}
x |= uint64(b&0x7f) << s
s += 7
}
panic("unreachable")
}
// WriteVarint encodes x and writes the result to w.
func WriteVarint(w io.Writer, x int64) os.Error {
ux := uint64(x) << 1
if x < 0 {
ux = ^ux
}
return WriteUvarint(w, ux)
}
// ReadVarint reads an encoded unsigned integer from r and returns it as a uint64.
func ReadVarint(r io.ByteReader) (int64, os.Error) {
ux, err := ReadUvarint(r) // ok to continue in presence of error
x := int64(ux >> 1)
if ux&1 != 0 {
x = ^x
}
return x, err
}

View file

@ -0,0 +1,182 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package binary
import (
"bytes"
"os"
"testing"
)
func testConstant(t *testing.T, w uint, max int) {
buf := make([]byte, MaxVarintLen64)
n := PutUvarint(buf, 1<<w-1)
if n != max {
t.Errorf("MaxVarintLen%d = %d; want %d", w, max, n)
}
}
func TestConstants(t *testing.T) {
testConstant(t, 16, MaxVarintLen16)
testConstant(t, 32, MaxVarintLen32)
testConstant(t, 64, MaxVarintLen64)
}
func testVarint(t *testing.T, x int64) {
buf1 := make([]byte, MaxVarintLen64)
n := PutVarint(buf1[:], x)
y, m := Varint(buf1[0:n])
if x != y {
t.Errorf("Varint(%d): got %d", x, y)
}
if n != m {
t.Errorf("Varint(%d): got n = %d; want %d", x, m, n)
}
var buf2 bytes.Buffer
err := WriteVarint(&buf2, x)
if err != nil {
t.Errorf("WriteVarint(%d): %s", x, err)
}
if n != buf2.Len() {
t.Errorf("WriteVarint(%d): got n = %d; want %d", x, buf2.Len(), n)
}
y, err = ReadVarint(&buf2)
if err != nil {
t.Errorf("ReadVarint(%d): %s", x, err)
}
if x != y {
t.Errorf("ReadVarint(%d): got %d", x, y)
}
}
func testUvarint(t *testing.T, x uint64) {
buf1 := make([]byte, MaxVarintLen64)
n := PutUvarint(buf1[:], x)
y, m := Uvarint(buf1[0:n])
if x != y {
t.Errorf("Uvarint(%d): got %d", x, y)
}
if n != m {
t.Errorf("Uvarint(%d): got n = %d; want %d", x, m, n)
}
var buf2 bytes.Buffer
err := WriteUvarint(&buf2, x)
if err != nil {
t.Errorf("WriteUvarint(%d): %s", x, err)
}
if n != buf2.Len() {
t.Errorf("WriteUvarint(%d): got n = %d; want %d", x, buf2.Len(), n)
}
y, err = ReadUvarint(&buf2)
if err != nil {
t.Errorf("ReadUvarint(%d): %s", x, err)
}
if x != y {
t.Errorf("ReadUvarint(%d): got %d", x, y)
}
}
var tests = []int64{
-1 << 63,
-1<<63 + 1,
-1,
0,
1,
2,
10,
20,
63,
64,
65,
127,
128,
129,
255,
256,
257,
1<<63 - 1,
}
func TestVarint(t *testing.T) {
for _, x := range tests {
testVarint(t, x)
testVarint(t, -x)
}
for x := int64(0x7); x != 0; x <<= 1 {
testVarint(t, x)
testVarint(t, -x)
}
}
func TestUvarint(t *testing.T) {
for _, x := range tests {
testUvarint(t, uint64(x))
}
for x := uint64(0x7); x != 0; x <<= 1 {
testUvarint(t, x)
}
}
func TestBufferTooSmall(t *testing.T) {
buf := []byte{0x80, 0x80, 0x80, 0x80}
for i := 0; i <= len(buf); i++ {
buf := buf[0:i]
x, n := Uvarint(buf)
if x != 0 || n != 0 {
t.Errorf("Uvarint(%v): got x = %d, n = %d", buf, x, n)
}
x, err := ReadUvarint(bytes.NewBuffer(buf))
if x != 0 || err != os.EOF {
t.Errorf("ReadUvarint(%v): got x = %d, err = %s", buf, x, err)
}
}
}
func testOverflow(t *testing.T, buf []byte, n0 int, err0 os.Error) {
x, n := Uvarint(buf)
if x != 0 || n != n0 {
t.Errorf("Uvarint(%v): got x = %d, n = %d; want 0, %d", buf, x, n, n0)
}
x, err := ReadUvarint(bytes.NewBuffer(buf))
if x != 0 || err != err0 {
t.Errorf("ReadUvarint(%v): got x = %d, err = %s; want 0, %s", buf, x, err, err0)
}
}
func TestOverflow(t *testing.T) {
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x2}, -10, overflow)
testOverflow(t, []byte{0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1, 0, 0}, -13, overflow)
}
func TestNonCanonicalZero(t *testing.T) {
buf := []byte{0x80, 0x80, 0x80, 0}
x, n := Uvarint(buf)
if x != 0 || n != 4 {
t.Errorf("Uvarint(%v): got x = %d, n = %d; want 0, 4", buf, x, n)
}
}
func BenchmarkPutUvarint32(b *testing.B) {
buf := make([]byte, MaxVarintLen32)
for i := 0; i < b.N; i++ {
for j := uint(0); j < MaxVarintLen32; j++ {
PutUvarint(buf, 1<<(j*7))
}
}
}
func BenchmarkPutUvarint64(b *testing.B) {
buf := make([]byte, MaxVarintLen64)
for i := 0; i < b.N; i++ {
for j := uint(0); j < MaxVarintLen64; j++ {
PutUvarint(buf, 1<<(j*7))
}
}
}

View file

@ -63,6 +63,11 @@ type Cmd struct {
Stdout io.Writer
Stderr io.Writer
// ExtraFiles specifies additional open files to be inherited by the
// new process. It does not include standard input, standard output, or
// standard error. If non-nil, entry i becomes file descriptor 3+i.
ExtraFiles []*os.File
// SysProcAttr holds optional, operating system-specific attributes.
// Run passes it to os.StartProcess as the os.ProcAttr's Sys field.
SysProcAttr *syscall.SysProcAttr
@ -224,6 +229,7 @@ func (c *Cmd) Start() os.Error {
}
c.childFiles = append(c.childFiles, fd)
}
c.childFiles = append(c.childFiles, c.ExtraFiles...)
var err os.Error
c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{

View file

@ -9,8 +9,10 @@ import (
"bytes"
"fmt"
"io"
"io/ioutil"
"testing"
"os"
"runtime"
"strconv"
"strings"
)
@ -139,6 +141,39 @@ func TestPipes(t *testing.T) {
check("Wait", err)
}
func TestExtraFiles(t *testing.T) {
if runtime.GOOS == "windows" {
t.Logf("no operating system support; skipping")
return
}
tf, err := ioutil.TempFile("", "")
if err != nil {
t.Fatalf("TempFile: %v", err)
}
defer os.Remove(tf.Name())
defer tf.Close()
const text = "Hello, fd 3!"
_, err = tf.Write([]byte(text))
if err != nil {
t.Fatalf("Write: %v", err)
}
_, err = tf.Seek(0, os.SEEK_SET)
if err != nil {
t.Fatalf("Seek: %v", err)
}
c := helperCommand("read3")
c.ExtraFiles = []*os.File{tf}
bs, err := c.CombinedOutput()
if err != nil {
t.Fatalf("CombinedOutput: %v", err)
}
if string(bs) != text {
t.Errorf("got %q; want %q", string(bs), text)
}
}
// TestHelperProcess isn't a real test. It's used as a helper process
// for TestParameterRun.
func TestHelperProcess(*testing.T) {
@ -204,6 +239,14 @@ func TestHelperProcess(*testing.T) {
os.Exit(1)
}
}
case "read3": // read fd 3
fd3 := os.NewFile(3, "fd3")
bs, err := ioutil.ReadAll(fd3)
if err != nil {
fmt.Printf("ReadAll from fd 3: %v", err)
os.Exit(1)
}
os.Stderr.Write(bs)
case "exit":
n, _ := strconv.Atoi(args[0])
os.Exit(n)

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin freebsd linux openbsd
package exec
import (

View file

@ -23,13 +23,39 @@
package ebnf
import (
"go/scanner"
"go/token"
"fmt"
"os"
"scanner"
"unicode"
"utf8"
)
// ----------------------------------------------------------------------------
// Error handling
type errorList []os.Error
func (list errorList) Error() os.Error {
if len(list) == 0 {
return nil
}
return list
}
func (list errorList) String() string {
switch len(list) {
case 0:
return "no errors"
case 1:
return list[0].String()
}
return fmt.Sprintf("%s (and %d more errors)", list[0], len(list)-1)
}
func newError(pos scanner.Position, msg string) os.Error {
return os.NewError(fmt.Sprintf("%s: %s", pos, msg))
}
// ----------------------------------------------------------------------------
// Internal representation
@ -37,7 +63,7 @@ type (
// An Expression node represents a production expression.
Expression interface {
// Pos is the position of the first character of the syntactic construct
Pos() token.Pos
Pos() scanner.Position
}
// An Alternative node represents a non-empty list of alternative expressions.
@ -48,13 +74,13 @@ type (
// A Name node represents a production name.
Name struct {
StringPos token.Pos
StringPos scanner.Position
String string
}
// A Token node represents a literal.
Token struct {
StringPos token.Pos
StringPos scanner.Position
String string
}
@ -65,50 +91,50 @@ type (
// A Group node represents a grouped expression.
Group struct {
Lparen token.Pos
Lparen scanner.Position
Body Expression // (body)
}
// An Option node represents an optional expression.
Option struct {
Lbrack token.Pos
Lbrack scanner.Position
Body Expression // [body]
}
// A Repetition node represents a repeated expression.
Repetition struct {
Lbrace token.Pos
Lbrace scanner.Position
Body Expression // {body}
}
// A Bad node stands for pieces of source code that lead to a parse error.
Bad struct {
TokPos token.Pos
Error string // parser error message
}
// A Production node represents an EBNF production.
Production struct {
Name *Name
Expr Expression
}
// A Bad node stands for pieces of source code that lead to a parse error.
Bad struct {
TokPos scanner.Position
Error string // parser error message
}
// A Grammar is a set of EBNF productions. The map
// is indexed by production name.
//
Grammar map[string]*Production
)
func (x Alternative) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Alternative
func (x Sequence) Pos() token.Pos { return x[0].Pos() } // the parser always generates non-empty Sequences
func (x *Name) Pos() token.Pos { return x.StringPos }
func (x *Token) Pos() token.Pos { return x.StringPos }
func (x *Range) Pos() token.Pos { return x.Begin.Pos() }
func (x *Group) Pos() token.Pos { return x.Lparen }
func (x *Option) Pos() token.Pos { return x.Lbrack }
func (x *Repetition) Pos() token.Pos { return x.Lbrace }
func (x *Bad) Pos() token.Pos { return x.TokPos }
func (x *Production) Pos() token.Pos { return x.Name.Pos() }
func (x Alternative) Pos() scanner.Position { return x[0].Pos() } // the parser always generates non-empty Alternative
func (x Sequence) Pos() scanner.Position { return x[0].Pos() } // the parser always generates non-empty Sequences
func (x *Name) Pos() scanner.Position { return x.StringPos }
func (x *Token) Pos() scanner.Position { return x.StringPos }
func (x *Range) Pos() scanner.Position { return x.Begin.Pos() }
func (x *Group) Pos() scanner.Position { return x.Lparen }
func (x *Option) Pos() scanner.Position { return x.Lbrack }
func (x *Repetition) Pos() scanner.Position { return x.Lbrace }
func (x *Production) Pos() scanner.Position { return x.Name.Pos() }
func (x *Bad) Pos() scanner.Position { return x.TokPos }
// ----------------------------------------------------------------------------
// Grammar verification
@ -119,15 +145,14 @@ func isLexical(name string) bool {
}
type verifier struct {
fset *token.FileSet
scanner.ErrorVector
errors errorList
worklist []*Production
reached Grammar // set of productions reached from (and including) the root production
grammar Grammar
}
func (v *verifier) error(pos token.Pos, msg string) {
v.Error(v.fset.Position(pos), msg)
func (v *verifier) error(pos scanner.Position, msg string) {
v.errors = append(v.errors, newError(pos, msg))
}
func (v *verifier) push(prod *Production) {
@ -187,24 +212,23 @@ func (v *verifier) verifyExpr(expr Expression, lexical bool) {
v.verifyExpr(x.Body, lexical)
case *Repetition:
v.verifyExpr(x.Body, lexical)
case *Bad:
v.error(x.Pos(), x.Error)
default:
panic("unreachable")
panic(fmt.Sprintf("internal error: unexpected type %T", expr))
}
}
func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) {
func (v *verifier) verify(grammar Grammar, start string) {
// find root production
root, found := grammar[start]
if !found {
// token.NoPos doesn't require a file set;
// ok to set v.fset only afterwards
v.error(token.NoPos, "no start production "+start)
var noPos scanner.Position
v.error(noPos, "no start production "+start)
return
}
// initialize verifier
v.fset = fset
v.ErrorVector.Reset()
v.worklist = v.worklist[0:0]
v.reached = make(Grammar)
v.grammar = grammar
@ -238,8 +262,8 @@ func (v *verifier) verify(fset *token.FileSet, grammar Grammar, start string) {
//
// Position information is interpreted relative to the file set fset.
//
func Verify(fset *token.FileSet, grammar Grammar, start string) os.Error {
func Verify(grammar Grammar, start string) os.Error {
var v verifier
v.verify(fset, grammar, start)
return v.GetError(scanner.Sorted)
v.verify(grammar, start)
return v.errors.Error()
}

View file

@ -5,13 +5,10 @@
package ebnf
import (
"go/token"
"io/ioutil"
"bytes"
"testing"
)
var fset = token.NewFileSet()
var goodGrammars = []string{
`Program = .`,
@ -46,18 +43,19 @@ var badGrammars = []string{
`Program = {} .`,
}
func checkGood(t *testing.T, filename string, src []byte) {
grammar, err := Parse(fset, filename, src)
func checkGood(t *testing.T, src string) {
grammar, err := Parse("", bytes.NewBuffer([]byte(src)))
if err != nil {
t.Errorf("Parse(%s) failed: %v", src, err)
return
}
if err = Verify(fset, grammar, "Program"); err != nil {
if err = Verify(grammar, "Program"); err != nil {
t.Errorf("Verify(%s) failed: %v", src, err)
}
}
func checkBad(t *testing.T, filename string, src []byte) {
_, err := Parse(fset, filename, src)
func checkBad(t *testing.T, src string) {
_, err := Parse("", bytes.NewBuffer([]byte(src)))
if err == nil {
t.Errorf("Parse(%s) should have failed", src)
}
@ -65,23 +63,9 @@ func checkBad(t *testing.T, filename string, src []byte) {
func TestGrammars(t *testing.T) {
for _, src := range goodGrammars {
checkGood(t, "", []byte(src))
checkGood(t, src)
}
for _, src := range badGrammars {
checkBad(t, "", []byte(src))
}
}
var files = []string{
// TODO(gri) add some test files
}
func TestFiles(t *testing.T) {
for _, filename := range files {
src, err := ioutil.ReadFile(filename)
if err != nil {
t.Fatal(err)
}
checkGood(t, filename, src)
checkBad(t, src)
}
}

View file

@ -5,51 +5,47 @@
package ebnf
import (
"go/scanner"
"go/token"
"io"
"os"
"scanner"
"strconv"
)
type parser struct {
fset *token.FileSet
scanner.ErrorVector
errors errorList
scanner scanner.Scanner
pos token.Pos // token position
tok token.Token // one token look-ahead
lit string // token literal
pos scanner.Position // token position
tok int // one token look-ahead
lit string // token literal
}
func (p *parser) next() {
p.pos, p.tok, p.lit = p.scanner.Scan()
if p.tok.IsKeyword() {
// TODO Should keyword mapping always happen outside scanner?
// Or should there be a flag to scanner to enable keyword mapping?
p.tok = token.IDENT
}
p.tok = p.scanner.Scan()
p.pos = p.scanner.Position
p.lit = p.scanner.TokenText()
}
func (p *parser) error(pos token.Pos, msg string) {
p.Error(p.fset.Position(pos), msg)
func (p *parser) error(pos scanner.Position, msg string) {
p.errors = append(p.errors, newError(pos, msg))
}
func (p *parser) errorExpected(pos token.Pos, msg string) {
msg = "expected " + msg
if pos == p.pos {
func (p *parser) errorExpected(pos scanner.Position, msg string) {
msg = `expected "` + msg + `"`
if pos.Offset == p.pos.Offset {
// the error happened at the current position;
// make the error message more specific
msg += ", found '" + p.tok.String() + "'"
if p.tok.IsLiteral() {
msg += ", found " + scanner.TokenString(p.tok)
if p.tok < 0 {
msg += " " + p.lit
}
}
p.error(pos, msg)
}
func (p *parser) expect(tok token.Token) token.Pos {
func (p *parser) expect(tok int) scanner.Position {
pos := p.pos
if p.tok != tok {
p.errorExpected(pos, "'"+tok.String()+"'")
p.errorExpected(pos, scanner.TokenString(tok))
}
p.next() // make progress in any case
return pos
@ -58,21 +54,21 @@ func (p *parser) expect(tok token.Token) token.Pos {
func (p *parser) parseIdentifier() *Name {
pos := p.pos
name := p.lit
p.expect(token.IDENT)
p.expect(scanner.Ident)
return &Name{pos, name}
}
func (p *parser) parseToken() *Token {
pos := p.pos
value := ""
if p.tok == token.STRING {
if p.tok == scanner.String {
value, _ = strconv.Unquote(p.lit)
// Unquote may fail with an error, but only if the scanner found
// an illegal string in the first place. In this case the error
// has already been reported.
p.next()
} else {
p.expect(token.STRING)
p.expect(scanner.String)
}
return &Token{pos, value}
}
@ -82,32 +78,32 @@ func (p *parser) parseTerm() (x Expression) {
pos := p.pos
switch p.tok {
case token.IDENT:
case scanner.Ident:
x = p.parseIdentifier()
case token.STRING:
case scanner.String:
tok := p.parseToken()
x = tok
const ellipsis = "…" // U+2026, the horizontal ellipsis character
if p.tok == token.ILLEGAL && p.lit == ellipsis {
const ellipsis = '…' // U+2026, the horizontal ellipsis character
if p.tok == ellipsis {
p.next()
x = &Range{tok, p.parseToken()}
}
case token.LPAREN:
case '(':
p.next()
x = &Group{pos, p.parseExpression()}
p.expect(token.RPAREN)
p.expect(')')
case token.LBRACK:
case '[':
p.next()
x = &Option{pos, p.parseExpression()}
p.expect(token.RBRACK)
p.expect(']')
case token.LBRACE:
case '{':
p.next()
x = &Repetition{pos, p.parseExpression()}
p.expect(token.RBRACE)
p.expect('}')
}
return x
@ -137,7 +133,7 @@ func (p *parser) parseExpression() Expression {
for {
list = append(list, p.parseSequence())
if p.tok != token.OR {
if p.tok != '|' {
break
}
p.next()
@ -154,24 +150,22 @@ func (p *parser) parseExpression() Expression {
func (p *parser) parseProduction() *Production {
name := p.parseIdentifier()
p.expect(token.ASSIGN)
p.expect('=')
var expr Expression
if p.tok != token.PERIOD {
if p.tok != '.' {
expr = p.parseExpression()
}
p.expect(token.PERIOD)
p.expect('.')
return &Production{name, expr}
}
func (p *parser) parse(fset *token.FileSet, filename string, src []byte) Grammar {
// initialize parser
p.fset = fset
p.ErrorVector.Reset()
p.scanner.Init(fset.AddFile(filename, fset.Base(), len(src)), src, p, scanner.AllowIllegalChars)
func (p *parser) parse(filename string, src io.Reader) Grammar {
p.scanner.Init(src)
p.scanner.Filename = filename
p.next() // initializes pos, tok, lit
grammar := make(Grammar)
for p.tok != token.EOF {
for p.tok != scanner.EOF {
prod := p.parseProduction()
name := prod.Name.String
if _, found := grammar[name]; !found {
@ -187,11 +181,11 @@ func (p *parser) parse(fset *token.FileSet, filename string, src []byte) Grammar
// Parse parses a set of EBNF productions from source src.
// It returns a set of productions. Errors are reported
// for incorrect syntax and if a production is declared
// more than once. Position information is recorded relative
// to the file set fset.
// more than once; the filename is used only for error
// positions.
//
func Parse(fset *token.FileSet, filename string, src []byte) (Grammar, os.Error) {
func Parse(filename string, src io.Reader) (Grammar, os.Error) {
var p parser
grammar := p.parse(fset, filename, src)
return grammar, p.GetError(scanner.Sorted)
grammar := p.parse(filename, src)
return grammar, p.errors.Error()
}

View file

@ -0,0 +1,22 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Ebnflint verifies that EBNF productions are consistent and gramatically correct.
It reads them from an HTML document such as the Go specification.
Grammar productions are grouped in boxes demarcated by the HTML elements
<pre class="ebnf">
</pre>
Usage:
ebnflint [--start production] [file]
The --start flag specifies the name of the start production for
the grammar; it defaults to "Start".
*/
package documentation

View file

@ -0,0 +1,109 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"exp/ebnf"
"flag"
"fmt"
"go/scanner"
"go/token"
"io/ioutil"
"os"
"path/filepath"
)
var fset = token.NewFileSet()
var start = flag.String("start", "Start", "name of start production")
func usage() {
fmt.Fprintf(os.Stderr, "usage: ebnflint [flags] [filename]\n")
flag.PrintDefaults()
os.Exit(1)
}
// Markers around EBNF sections in .html files
var (
open = []byte(`<pre class="ebnf">`)
close = []byte(`</pre>`)
)
func report(err os.Error) {
scanner.PrintError(os.Stderr, err)
os.Exit(1)
}
func extractEBNF(src []byte) []byte {
var buf bytes.Buffer
for {
// i = beginning of EBNF text
i := bytes.Index(src, open)
if i < 0 {
break // no EBNF found - we are done
}
i += len(open)
// write as many newlines as found in the excluded text
// to maintain correct line numbers in error messages
for _, ch := range src[0:i] {
if ch == '\n' {
buf.WriteByte('\n')
}
}
// j = end of EBNF text (or end of source)
j := bytes.Index(src[i:], close) // close marker
if j < 0 {
j = len(src) - i
}
j += i
// copy EBNF text
buf.Write(src[i:j])
// advance
src = src[j:]
}
return buf.Bytes()
}
func main() {
flag.Parse()
var (
filename string
src []byte
err os.Error
)
switch flag.NArg() {
case 0:
filename = "<stdin>"
src, err = ioutil.ReadAll(os.Stdin)
case 1:
filename = flag.Arg(0)
src, err = ioutil.ReadFile(filename)
default:
usage()
}
if err != nil {
report(err)
}
if filepath.Ext(filename) == ".html" || bytes.Index(src, open) >= 0 {
src = extractEBNF(src)
}
grammar, err := ebnf.Parse(filename, bytes.NewBuffer(src))
if err != nil {
report(err)
}
if err = ebnf.Verify(grammar, *start); err != nil {
report(err)
}
}

View file

@ -0,0 +1,61 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
The gotype command does syntactic and semantic analysis of Go files
and packages similar to the analysis performed by the front-end of
a Go compiler. Errors are reported if the analysis fails; otherwise
gotype is quiet (unless -v is set).
Without a list of paths, gotype processes the standard input, which must
be the source of a single package file.
Given a list of file names, each file must be a source file belonging to
the same package unless the package name is explicitly specified with the
-p flag.
Given a directory name, gotype collects all .go files in the directory
and processes them as if they were provided as an explicit list of file
names. Each directory is processed independently. Files starting with .
or not ending in .go are ignored.
Usage:
gotype [flags] [path ...]
The flags are:
-e
Print all (including spurious) errors.
-p pkgName
Process only those files in package pkgName.
-r
Recursively process subdirectories.
-v
Verbose mode.
Debugging flags:
-ast
Print AST (disables concurrent parsing).
-trace
Print parse trace (disables concurrent parsing).
Examples
To check the files file.go, old.saved, and .ignored:
gotype file.go old.saved .ignored
To check all .go files belonging to package main in the current directory
and recursively in all subdirectories:
gotype -p main -r .
To verify the output of a pipe:
echo "package foo" | gotype
*/
package documentation
// BUG(gri): At the moment, only single-file scope analysis is performed.

View file

@ -0,0 +1,192 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"exp/types"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var (
// main operation modes
pkgName = flag.String("p", "", "process only those files in package pkgName")
recursive = flag.Bool("r", false, "recursively process subdirectories")
verbose = flag.Bool("v", false, "verbose mode")
allErrors = flag.Bool("e", false, "print all (including spurious) errors")
// debugging support
printTrace = flag.Bool("trace", false, "print parse trace")
printAST = flag.Bool("ast", false, "print AST")
)
var exitCode = 0
func usage() {
fmt.Fprintf(os.Stderr, "usage: gotype [flags] [path ...]\n")
flag.PrintDefaults()
os.Exit(2)
}
func report(err os.Error) {
scanner.PrintError(os.Stderr, err)
exitCode = 2
}
// parse returns the AST for the Go source src.
// The filename is for error reporting only.
// The result is nil if there were errors or if
// the file does not belong to the -p package.
func parse(fset *token.FileSet, filename string, src []byte) *ast.File {
if *verbose {
fmt.Println(filename)
}
// ignore files with different package name
if *pkgName != "" {
file, err := parser.ParseFile(fset, filename, src, parser.PackageClauseOnly)
if err != nil {
report(err)
return nil
}
if file.Name.Name != *pkgName {
if *verbose {
fmt.Printf("\tignored (package %s)\n", file.Name.Name)
}
return nil
}
}
// parse entire file
mode := parser.DeclarationErrors
if *allErrors {
mode |= parser.SpuriousErrors
}
if *printTrace {
mode |= parser.Trace
}
file, err := parser.ParseFile(fset, filename, src, mode)
if err != nil {
report(err)
return nil
}
if *printAST {
ast.Print(fset, file)
}
return file
}
func parseStdin(fset *token.FileSet) (files map[string]*ast.File) {
files = make(map[string]*ast.File)
src, err := ioutil.ReadAll(os.Stdin)
if err != nil {
report(err)
return
}
const filename = "<standard input>"
if file := parse(fset, filename, src); file != nil {
files[filename] = file
}
return
}
func parseFiles(fset *token.FileSet, filenames []string) (files map[string]*ast.File) {
files = make(map[string]*ast.File)
for _, filename := range filenames {
src, err := ioutil.ReadFile(filename)
if err != nil {
report(err)
continue
}
if file := parse(fset, filename, src); file != nil {
if files[filename] != nil {
report(os.NewError(fmt.Sprintf("%q: duplicate file", filename)))
continue
}
files[filename] = file
}
}
return
}
func isGoFilename(filename string) bool {
// ignore non-Go files
return !strings.HasPrefix(filename, ".") && strings.HasSuffix(filename, ".go")
}
func processDirectory(dirname string) {
f, err := os.Open(dirname)
if err != nil {
report(err)
return
}
filenames, err := f.Readdirnames(-1)
f.Close()
if err != nil {
report(err)
// continue since filenames may not be empty
}
for i, filename := range filenames {
filenames[i] = filepath.Join(dirname, filename)
}
processFiles(filenames, false)
}
func processFiles(filenames []string, allFiles bool) {
i := 0
for _, filename := range filenames {
switch info, err := os.Stat(filename); {
case err != nil:
report(err)
case info.IsRegular():
if allFiles || isGoFilename(info.Name) {
filenames[i] = filename
i++
}
case info.IsDirectory():
if allFiles || *recursive {
processDirectory(filename)
}
}
}
fset := token.NewFileSet()
processPackage(fset, parseFiles(fset, filenames[0:i]))
}
func processPackage(fset *token.FileSet, files map[string]*ast.File) {
// make a package (resolve all identifiers)
pkg, err := ast.NewPackage(fset, files, types.GcImporter, types.Universe)
if err != nil {
report(err)
return
}
_, err = types.Check(fset, pkg)
if err != nil {
report(err)
}
}
func main() {
flag.Usage = usage
flag.Parse()
if flag.NArg() == 0 {
fset := token.NewFileSet()
processPackage(fset, parseStdin(fset))
} else {
processFiles(flag.Args(), true)
}
os.Exit(exitCode)
}

View file

@ -0,0 +1,49 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"path/filepath"
"runtime"
"testing"
)
func runTest(t *testing.T, path, pkg string) {
exitCode = 0
*pkgName = pkg
*recursive = false
if pkg == "" {
processFiles([]string{path}, true)
} else {
processDirectory(path)
}
if exitCode != 0 {
t.Errorf("processing %s failed: exitCode = %d", path, exitCode)
}
}
var tests = []struct {
path string
pkg string
}{
// individual files
{"testdata/test1.go", ""},
// directories
{filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
{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/exp/types"), "types"},
}
func Test(t *testing.T) {
for _, test := range tests {
runTest(t, test.path, test.pkg)
}
}

23
libgo/go/exp/gotype/testdata/test1.go vendored Normal file
View file

@ -0,0 +1,23 @@
package p
func _() {
// the scope of a local type declaration starts immediately after the type name
type T struct{ _ *T }
}
func _(x interface{}) {
// the variable defined by a TypeSwitchGuard is declared in each TypeCaseClause
switch t := x.(type) {
case int:
_ = t
case float32:
_ = t
default:
_ = t
}
// the variable defined by a TypeSwitchGuard must not conflict with other
// variables declared in the initial simple statement
switch t := 0; t := x.(type) {
}
}

View file

@ -621,7 +621,7 @@ func NewWindowDisplay(display string) (gui.Window, os.Error) {
return nil, err
}
c.img = image.NewRGBA(windowWidth, windowHeight)
c.img = image.NewRGBA(image.Rect(0, 0, windowWidth, windowHeight))
c.eventc = make(chan interface{}, 16)
c.flush = make(chan bool, 1)
go c.readSocket()

View file

@ -7,27 +7,46 @@ package norm
import "utf8"
const (
maxCombiningChars = 30 + 2 // +2 to hold CGJ and Hangul overflow.
maxCombiningChars = 30
maxBufferSize = maxCombiningChars + 2 // +1 to hold starter +1 to hold CGJ
maxBackRunes = maxCombiningChars - 1
maxNFCExpansion = 3 // NFC(0x1D160)
maxNFKCExpansion = 18 // NFKC(0xFDFA)
maxRuneSizeInDecomp = 4
// Need to multiply by 2 as we don't reuse byte buffer space for recombining.
maxByteBufferSize = 2 * maxRuneSizeInDecomp * maxCombiningChars // 256
maxByteBufferSize = utf8.UTFMax * maxBufferSize // 128
)
// reorderBuffer is used to normalize a single segment. Characters inserted with
// insert() are decomposed and reordered based on CCC. The compose() method can
// insert are decomposed and reordered based on CCC. The compose method can
// be used to recombine characters. Note that the byte buffer does not hold
// the UTF-8 characters in order. Only the rune array is maintained in sorted
// order. flush() writes the resulting segment to a byte array.
// order. flush writes the resulting segment to a byte array.
type reorderBuffer struct {
rune [maxCombiningChars]runeInfo // Per character info.
byte [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos.
nrune int // Number of runeInfos.
nbyte uint8 // Number or bytes.
rune [maxBufferSize]runeInfo // Per character info.
byte [maxByteBufferSize]byte // UTF-8 buffer. Referenced by runeInfo.pos.
nrune int // Number of runeInfos.
nbyte uint8 // Number or bytes.
f formInfo
src input
nsrc int
srcBytes inputBytes
srcString inputString
tmpBytes inputBytes
}
func (rb *reorderBuffer) init(f Form, src []byte) {
rb.f = *formTable[f]
rb.srcBytes = inputBytes(src)
rb.src = &rb.srcBytes
rb.nsrc = len(src)
}
func (rb *reorderBuffer) initString(f Form, src string) {
rb.f = *formTable[f]
rb.srcString = inputString(src)
rb.src = &rb.srcString
rb.nsrc = len(src)
}
// reset discards all characters from the buffer.
@ -49,10 +68,10 @@ func (rb *reorderBuffer) flush(out []byte) []byte {
// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
// It returns false if the buffer is not large enough to hold the rune.
// It is used internally by insert.
// It is used internally by insert and insertString only.
func (rb *reorderBuffer) insertOrdered(info runeInfo) bool {
n := rb.nrune
if n >= maxCombiningChars {
if n >= maxCombiningChars+1 {
return false
}
b := rb.rune[:]
@ -68,7 +87,7 @@ func (rb *reorderBuffer) insertOrdered(info runeInfo) bool {
}
rb.nrune += 1
pos := uint8(rb.nbyte)
rb.nbyte += info.size
rb.nbyte += utf8.UTFMax
info.pos = pos
b[n] = info
return true
@ -76,53 +95,32 @@ func (rb *reorderBuffer) insertOrdered(info runeInfo) bool {
// insert inserts the given rune in the buffer ordered by CCC.
// It returns true if the buffer was large enough to hold the decomposed rune.
func (rb *reorderBuffer) insert(src []byte, info runeInfo) bool {
if info.size == 3 && isHangul(src) {
rune, _ := utf8.DecodeRune(src)
return rb.decomposeHangul(uint32(rune))
func (rb *reorderBuffer) insert(src input, i int, info runeInfo) bool {
if info.size == 3 {
if rune := src.hangul(i); rune != 0 {
return rb.decomposeHangul(uint32(rune))
}
}
pos := rb.nbyte
if info.flags.hasDecomposition() {
dcomp := rb.f.decompose(src)
for i := 0; i < len(dcomp); i += int(info.size) {
info = rb.f.info(dcomp[i:])
dcomp := rb.f.decompose(src, i)
rb.tmpBytes = inputBytes(dcomp)
for i := 0; i < len(dcomp); {
info = rb.f.info(&rb.tmpBytes, i)
pos := rb.nbyte
if !rb.insertOrdered(info) {
return false
}
end := i + int(info.size)
copy(rb.byte[pos:], dcomp[i:end])
i = end
}
copy(rb.byte[pos:], dcomp)
} else {
// insertOrder changes nbyte
pos := rb.nbyte
if !rb.insertOrdered(info) {
return false
}
copy(rb.byte[pos:], src[:info.size])
}
return true
}
// insertString inserts the given rune in the buffer ordered by CCC.
// It returns true if the buffer was large enough to hold the decomposed rune.
func (rb *reorderBuffer) insertString(src string, info runeInfo) bool {
if info.size == 3 && isHangulString(src) {
rune, _ := utf8.DecodeRuneInString(src)
return rb.decomposeHangul(uint32(rune))
}
pos := rb.nbyte
dcomp := rb.f.decomposeString(src)
dn := len(dcomp)
if dn != 0 {
for i := 0; i < dn; i += int(info.size) {
info = rb.f.info(dcomp[i:])
if !rb.insertOrdered(info) {
return false
}
}
copy(rb.byte[pos:], dcomp)
} else {
if !rb.insertOrdered(info) {
return false
}
copy(rb.byte[pos:], src[:info.size])
src.copySlice(rb.byte[pos:], i, i+int(info.size))
}
return true
}
@ -131,17 +129,16 @@ func (rb *reorderBuffer) insertString(src string, info runeInfo) bool {
func (rb *reorderBuffer) appendRune(rune uint32) {
bn := rb.nbyte
sz := utf8.EncodeRune(rb.byte[bn:], int(rune))
rb.nbyte += uint8(sz)
rb.nbyte += utf8.UTFMax
rb.rune[rb.nrune] = runeInfo{bn, uint8(sz), 0, 0}
rb.nrune++
}
// assignRune sets a rune at position pos. It is used for Hangul and recomposition.
func (rb *reorderBuffer) assignRune(pos int, rune uint32) {
bn := rb.nbyte
bn := rb.rune[pos].pos
sz := utf8.EncodeRune(rb.byte[bn:], int(rune))
rb.rune[pos] = runeInfo{bn, uint8(sz), 0, 0}
rb.nbyte += uint8(sz)
}
// runeAt returns the rune at position n. It is used for Hangul and recomposition.
@ -259,11 +256,10 @@ func (rb *reorderBuffer) decomposeHangul(rune uint32) bool {
// combineHangul algorithmically combines Jamo character components into Hangul.
// See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
func (rb *reorderBuffer) combineHangul() {
k := 1
func (rb *reorderBuffer) combineHangul(s, i, k int) {
b := rb.rune[:]
bn := rb.nrune
for s, i := 0, 1; i < bn; i++ {
for ; i < bn; i++ {
cccB := b[k-1].ccc
cccC := b[i].ccc
if cccB == 0 {
@ -305,14 +301,17 @@ func (rb *reorderBuffer) compose() {
// blocked from S if and only if there is some character B between S
// and C, and either B is a starter or it has the same or higher
// combining class as C."
bn := rb.nrune
if bn == 0 {
return
}
k := 1
b := rb.rune[:]
bn := rb.nrune
for s, i := 0, 1; i < bn; i++ {
if isJamoVT(rb.bytesAt(i)) {
// Redo from start in Hangul mode. Necessary to support
// U+320E..U+321E in NFKC mode.
rb.combineHangul()
rb.combineHangul(s, i, k)
return
}
ii := b[i]

View file

@ -15,21 +15,19 @@ type TestCase struct {
type insertFunc func(rb *reorderBuffer, rune int) bool
func insert(rb *reorderBuffer, rune int) bool {
b := []byte(string(rune))
return rb.insert(b, rb.f.info(b))
src := inputString(string(rune))
return rb.insert(src, 0, rb.f.info(src, 0))
}
func insertString(rb *reorderBuffer, rune int) bool {
s := string(rune)
return rb.insertString(s, rb.f.infoString(s))
}
func runTests(t *testing.T, name string, rb *reorderBuffer, f insertFunc, tests []TestCase) {
func runTests(t *testing.T, name string, fm Form, f insertFunc, tests []TestCase) {
rb := reorderBuffer{}
rb.init(fm, nil)
for i, test := range tests {
rb.reset()
for j, rune := range test.in {
b := []byte(string(rune))
if !rb.insert(b, rb.f.info(b)) {
src := inputBytes(b)
if !rb.insert(src, 0, rb.f.info(src, 0)) {
t.Errorf("%s:%d: insert failed for rune %d", name, i, j)
}
}
@ -50,7 +48,8 @@ func runTests(t *testing.T, name string, rb *reorderBuffer, f insertFunc, tests
}
func TestFlush(t *testing.T) {
rb := &reorderBuffer{f: *formTable[NFC]}
rb := reorderBuffer{}
rb.init(NFC, nil)
out := make([]byte, 0)
out = rb.flush(out)
@ -59,7 +58,7 @@ func TestFlush(t *testing.T) {
}
for _, r := range []int("world!") {
insert(rb, r)
insert(&rb, r)
}
out = []byte("Hello ")
@ -88,13 +87,7 @@ var insertTests = []TestCase{
}
func TestInsert(t *testing.T) {
rb := &reorderBuffer{f: *formTable[NFD]}
runTests(t, "TestInsert", rb, insert, insertTests)
}
func TestInsertString(t *testing.T) {
rb := &reorderBuffer{f: *formTable[NFD]}
runTests(t, "TestInsertString", rb, insertString, insertTests)
runTests(t, "TestInsert", NFD, insert, insertTests)
}
var decompositionNFDTest = []TestCase{
@ -113,11 +106,8 @@ var decompositionNFKDTest = []TestCase{
}
func TestDecomposition(t *testing.T) {
rb := &reorderBuffer{}
rb.f = *formTable[NFD]
runTests(t, "TestDecompositionNFD", rb, insert, decompositionNFDTest)
rb.f = *formTable[NFKD]
runTests(t, "TestDecompositionNFKD", rb, insert, decompositionNFKDTest)
runTests(t, "TestDecompositionNFD", NFD, insert, decompositionNFDTest)
runTests(t, "TestDecompositionNFKD", NFKD, insert, decompositionNFKDTest)
}
var compositionTest = []TestCase{
@ -133,6 +123,5 @@ var compositionTest = []TestCase{
}
func TestComposition(t *testing.T) {
rb := &reorderBuffer{f: *formTable[NFC]}
runTests(t, "TestComposition", rb, insert, compositionTest)
runTests(t, "TestComposition", NFC, insert, compositionTest)
}

View file

@ -15,10 +15,8 @@ type runeInfo struct {
// functions dispatchable per form
type boundaryFunc func(f *formInfo, info runeInfo) bool
type lookupFunc func(b []byte) runeInfo
type lookupFuncString func(s string) runeInfo
type decompFunc func(b []byte) []byte
type decompFuncString func(s string) []byte
type lookupFunc func(b input, i int) runeInfo
type decompFunc func(b input, i int) []byte
// formInfo holds Form-specific functions and tables.
type formInfo struct {
@ -26,12 +24,10 @@ type formInfo struct {
composing, compatibility bool // form type
decompose decompFunc
decomposeString decompFuncString
info lookupFunc
infoString lookupFuncString
boundaryBefore boundaryFunc
boundaryAfter boundaryFunc
decompose decompFunc
info lookupFunc
boundaryBefore boundaryFunc
boundaryAfter boundaryFunc
}
var formTable []*formInfo
@ -46,14 +42,10 @@ func init() {
if Form(i) == NFKD || Form(i) == NFKC {
f.compatibility = true
f.decompose = decomposeNFKC
f.decomposeString = decomposeStringNFKC
f.info = lookupInfoNFKC
f.infoString = lookupInfoStringNFKC
} else {
f.decompose = decomposeNFC
f.decomposeString = decomposeStringNFC
f.info = lookupInfoNFC
f.infoString = lookupInfoStringNFC
}
if Form(i) == NFC || Form(i) == NFKC {
f.composing = true
@ -77,7 +69,7 @@ func decompBoundary(f *formInfo, info runeInfo) bool {
}
func compBoundaryBefore(f *formInfo, info runeInfo) bool {
if info.ccc == 0 && info.flags.isYesC() {
if info.ccc == 0 && !info.flags.combinesBackward() {
return true
}
// We assume that the CCC of the first character in a decomposition
@ -89,9 +81,7 @@ func compBoundaryBefore(f *formInfo, info runeInfo) bool {
func compBoundaryAfter(f *formInfo, info runeInfo) bool {
// This misses values where the last char in a decomposition is a
// boundary such as Hangul with JamoT.
// TODO(mpvl): verify this does not lead to segments that do
// not fit in the reorderBuffer.
return info.flags.isInert()
return info.isInert()
}
// We pack quick check data in 4 bits:
@ -110,41 +100,30 @@ func (i qcInfo) isNoC() bool { return i&0x6 == 0x2 }
func (i qcInfo) isMaybe() bool { return i&0x4 != 0 }
func (i qcInfo) isYesD() bool { return i&0x1 == 0 }
func (i qcInfo) isNoD() bool { return i&0x1 != 0 }
func (i qcInfo) isInert() bool { return i&0xf == 0 }
func (i qcInfo) combinesForward() bool { return i&0x8 != 0 }
func (i qcInfo) combinesBackward() bool { return i&0x4 != 0 } // == isMaybe
func (i qcInfo) hasDecomposition() bool { return i&0x1 != 0 } // == isNoD
func (r runeInfo) isInert() bool {
return r.flags&0xf == 0 && r.ccc == 0
}
// Wrappers for tables.go
// The 16-bit value of the decompostion tries is an index into a byte
// array of UTF-8 decomposition sequences. The first byte is the number
// of bytes in the decomposition (excluding this length byte). The actual
// sequence starts at the offset+1.
func decomposeNFC(b []byte) []byte {
p := nfcDecompTrie.lookupUnsafe(b)
func decomposeNFC(s input, i int) []byte {
p := s.decomposeNFC(i)
n := decomps[p]
p++
return decomps[p : p+uint16(n)]
}
func decomposeNFKC(b []byte) []byte {
p := nfkcDecompTrie.lookupUnsafe(b)
n := decomps[p]
p++
return decomps[p : p+uint16(n)]
}
func decomposeStringNFC(s string) []byte {
p := nfcDecompTrie.lookupStringUnsafe(s)
n := decomps[p]
p++
return decomps[p : p+uint16(n)]
}
func decomposeStringNFKC(s string) []byte {
p := nfkcDecompTrie.lookupStringUnsafe(s)
func decomposeNFKC(s input, i int) []byte {
p := s.decomposeNFKC(i)
n := decomps[p]
p++
return decomps[p : p+uint16(n)]
@ -167,22 +146,12 @@ func combine(a, b uint32) uint32 {
// 0..7 CCC value.
// 8..11 qcInfo for NFC/NFD
// 12..15 qcInfo for NFKC/NFKD
func lookupInfoNFC(b []byte) runeInfo {
v, sz := charInfoTrie.lookup(b)
func lookupInfoNFC(b input, i int) runeInfo {
v, sz := b.charinfo(i)
return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 8)}
}
func lookupInfoStringNFC(s string) runeInfo {
v, sz := charInfoTrie.lookupString(s)
return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 8)}
}
func lookupInfoNFKC(b []byte) runeInfo {
v, sz := charInfoTrie.lookup(b)
return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 12)}
}
func lookupInfoStringNFKC(s string) runeInfo {
v, sz := charInfoTrie.lookupString(s)
func lookupInfoNFKC(b input, i int) runeInfo {
v, sz := b.charinfo(i)
return runeInfo{0, uint8(sz), uint8(v), qcInfo(v >> 12)}
}

107
libgo/go/exp/norm/input.go Normal file
View file

@ -0,0 +1,107 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package norm
import "utf8"
type input interface {
skipASCII(p int) int
skipNonStarter() int
appendSlice(buf []byte, s, e int) []byte
copySlice(buf []byte, s, e int)
charinfo(p int) (uint16, int)
decomposeNFC(p int) uint16
decomposeNFKC(p int) uint16
hangul(p int) uint32
}
type inputString string
func (s inputString) skipASCII(p int) int {
for ; p < len(s) && s[p] < utf8.RuneSelf; p++ {
}
return p
}
func (s inputString) skipNonStarter() int {
p := 0
for ; p < len(s) && !utf8.RuneStart(s[p]); p++ {
}
return p
}
func (s inputString) appendSlice(buf []byte, b, e int) []byte {
for i := b; i < e; i++ {
buf = append(buf, s[i])
}
return buf
}
func (s inputString) copySlice(buf []byte, b, e int) {
copy(buf, s[b:e])
}
func (s inputString) charinfo(p int) (uint16, int) {
return charInfoTrie.lookupString(string(s[p:]))
}
func (s inputString) decomposeNFC(p int) uint16 {
return nfcDecompTrie.lookupStringUnsafe(string(s[p:]))
}
func (s inputString) decomposeNFKC(p int) uint16 {
return nfkcDecompTrie.lookupStringUnsafe(string(s[p:]))
}
func (s inputString) hangul(p int) uint32 {
if !isHangulString(string(s[p:])) {
return 0
}
rune, _ := utf8.DecodeRuneInString(string(s[p:]))
return uint32(rune)
}
type inputBytes []byte
func (s inputBytes) skipASCII(p int) int {
for ; p < len(s) && s[p] < utf8.RuneSelf; p++ {
}
return p
}
func (s inputBytes) skipNonStarter() int {
p := 0
for ; p < len(s) && !utf8.RuneStart(s[p]); p++ {
}
return p
}
func (s inputBytes) appendSlice(buf []byte, b, e int) []byte {
return append(buf, s[b:e]...)
}
func (s inputBytes) copySlice(buf []byte, b, e int) {
copy(buf, s[b:e])
}
func (s inputBytes) charinfo(p int) (uint16, int) {
return charInfoTrie.lookup(s[p:])
}
func (s inputBytes) decomposeNFC(p int) uint16 {
return nfcDecompTrie.lookupUnsafe(s[p:])
}
func (s inputBytes) decomposeNFKC(p int) uint16 {
return nfkcDecompTrie.lookupUnsafe(s[p:])
}
func (s inputBytes) hangul(p int) uint32 {
if !isHangul(s[p:]) {
return 0
}
rune, _ := utf8.DecodeRune(s[p:])
return uint32(rune)
}

View file

@ -515,9 +515,13 @@ func completeCharFields(form int) {
f.quickCheck[MComposed] = QCNo
case (i & 0xffff00) == JamoLBase:
f.quickCheck[MComposed] = QCYes
if JamoLBase <= i && i < JamoLEnd {
f.combinesForward = true
}
if JamoVBase <= i && i < JamoVEnd {
f.quickCheck[MComposed] = QCMaybe
f.combinesBackward = true
f.combinesForward = true
}
if JamoTBase <= i && i < JamoTEnd {
f.quickCheck[MComposed] = QCMaybe
@ -562,7 +566,7 @@ func makeEntry(f *FormInfo) uint16 {
case QCMaybe:
e |= 0x6
default:
log.Fatalf("Illegal quickcheck value %d.", f.quickCheck[MComposed])
log.Fatalf("Illegal quickcheck value %v.", f.quickCheck[MComposed])
}
return e
}

View file

@ -21,6 +21,7 @@ var testRunes = []int{
0x80, 0x100, 0x7FF, // 2-byte sequences
0x800, 0x999, 0xFFFF, // 3-byte sequences
0x10000, 0x10101, 0x10FFFF, // 4-byte sequences
0x200, 0x201, 0x202, 0x210, 0x215, // five entries in one sparse block
}
const fileHeader = `// Generated by running

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