Update Go library to last weekly.
From-SVN: r180552
This commit is contained in:
parent
e0c39d66d4
commit
d8f412571f
537 changed files with 43765 additions and 12999 deletions
|
@ -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})
|
||||
}
|
||||
}()
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
BIN
libgo/go/archive/tar/testdata/writer.tar
vendored
BIN
libgo/go/archive/tar/testdata/writer.tar
vendored
Binary file not shown.
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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"}},
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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] }
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) }
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
38
libgo/go/crypto/bcrypt/base64.go
Normal file
38
libgo/go/crypto/bcrypt/base64.go
Normal 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
|
||||
}
|
282
libgo/go/crypto/bcrypt/bcrypt.go
Normal file
282
libgo/go/crypto/bcrypt/bcrypt.go
Normal 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
|
||||
}
|
195
libgo/go/crypto/bcrypt/bcrypt_test.go
Normal file
195
libgo/go/crypto/bcrypt/bcrypt_test.go
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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:])
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
95
libgo/go/crypto/tls/root_darwin.go
Normal file
95
libgo/go/crypto/tls/root_darwin.go
Normal 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
|
||||
}
|
8
libgo/go/crypto/tls/root_stub.go
Normal file
8
libgo/go/crypto/tls/root_stub.go
Normal 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() {
|
||||
}
|
36
libgo/go/crypto/tls/root_test.go
Normal file
36
libgo/go/crypto/tls/root_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
29
libgo/go/crypto/tls/root_unix.go
Normal file
29
libgo/go/crypto/tls/root_unix.go
Normal 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
|
||||
}
|
54
libgo/go/crypto/tls/root_windows.go
Normal file
54
libgo/go/crypto/tls/root_windows.go
Normal 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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
122
libgo/go/crypto/x509/pkcs1.go
Normal file
122
libgo/go/crypto/x509/pkcs1.go
Normal 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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
149
libgo/go/encoding/binary/varint.go
Normal file
149
libgo/go/encoding/binary/varint.go
Normal 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
|
||||
}
|
182
libgo/go/encoding/binary/varint_test.go
Normal file
182
libgo/go/encoding/binary/varint_test.go
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
22
libgo/go/exp/ebnflint/doc.go
Normal file
22
libgo/go/exp/ebnflint/doc.go
Normal 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
|
109
libgo/go/exp/ebnflint/ebnflint.go
Normal file
109
libgo/go/exp/ebnflint/ebnflint.go
Normal 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)
|
||||
}
|
||||
}
|
61
libgo/go/exp/gotype/doc.go
Normal file
61
libgo/go/exp/gotype/doc.go
Normal 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.
|
192
libgo/go/exp/gotype/gotype.go
Normal file
192
libgo/go/exp/gotype/gotype.go
Normal 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)
|
||||
}
|
49
libgo/go/exp/gotype/gotype_test.go
Normal file
49
libgo/go/exp/gotype/gotype_test.go
Normal 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
23
libgo/go/exp/gotype/testdata/test1.go
vendored
Normal 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) {
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
107
libgo/go/exp/norm/input.go
Normal 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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue