parent
72de8622ae
commit
08a680a887
|
@ -1,4 +1,4 @@
|
|||
2ccfd4b451d3
|
||||
5e806355a9e1
|
||||
|
||||
The first line of this file holds the Mercurial revision number of the
|
||||
last merge done from the master library sources.
|
||||
|
|
|
@ -32,6 +32,7 @@ const (
|
|||
hashSize = 1 << hashBits
|
||||
hashMask = (1 << hashBits) - 1
|
||||
hashShift = (hashBits + minMatchLength - 1) / minMatchLength
|
||||
maxHashOffset = 1 << 24
|
||||
|
||||
skipNever = math.MaxInt32
|
||||
)
|
||||
|
@ -106,6 +107,25 @@ func (d *compressor) fillDeflate(b []byte) int {
|
|||
d.blockStart = math.MaxInt32
|
||||
}
|
||||
d.hashOffset += windowSize
|
||||
if d.hashOffset > maxHashOffset {
|
||||
delta := d.hashOffset - 1
|
||||
d.hashOffset -= delta
|
||||
d.chainHead -= delta
|
||||
for i, v := range d.hashPrev {
|
||||
if v > delta {
|
||||
d.hashPrev[i] -= delta
|
||||
} else {
|
||||
d.hashPrev[i] = 0
|
||||
}
|
||||
}
|
||||
for i, v := range d.hashHead {
|
||||
if v > delta {
|
||||
d.hashHead[i] -= delta
|
||||
} else {
|
||||
d.hashHead[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
n := copy(d.window[d.windowEnd:], b)
|
||||
d.windowEnd += n
|
||||
|
|
|
@ -94,6 +94,50 @@ func TestDeflate(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// A sparseReader returns a stream consisting of 0s followed by 1<<16 1s.
|
||||
// This tests missing hash references in a very large input.
|
||||
type sparseReader struct {
|
||||
l int64
|
||||
cur int64
|
||||
}
|
||||
|
||||
func (r *sparseReader) Read(b []byte) (n int, err error) {
|
||||
if r.cur >= r.l {
|
||||
return 0, io.EOF
|
||||
}
|
||||
n = len(b)
|
||||
cur := r.cur + int64(n)
|
||||
if cur > r.l {
|
||||
n -= int(cur - r.l)
|
||||
cur = r.l
|
||||
}
|
||||
for i := range b[0:n] {
|
||||
if r.cur+int64(i) >= r.l-1<<16 {
|
||||
b[i] = 1
|
||||
} else {
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
r.cur = cur
|
||||
return
|
||||
}
|
||||
|
||||
func TestVeryLongSparseChunk(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Logf("skipping sparse chunk during short test")
|
||||
return
|
||||
}
|
||||
w, err := NewWriter(ioutil.Discard, 1)
|
||||
if err != nil {
|
||||
t.Errorf("NewWriter: %v", err)
|
||||
return
|
||||
}
|
||||
if _, err = io.Copy(w, &sparseReader{l: 23E8}); err != nil {
|
||||
t.Errorf("Compress failed: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type syncBuffer struct {
|
||||
buf bytes.Buffer
|
||||
mu sync.RWMutex
|
||||
|
|
|
@ -11,11 +11,11 @@ package aes
|
|||
// http://www.csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
||||
|
||||
// AES is based on the mathematical behavior of binary polynomials
|
||||
// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x² + x + 1.
|
||||
// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x³ + x + 1.
|
||||
// Addition of these binary polynomials corresponds to binary xor.
|
||||
// Reducing mod poly corresponds to binary xor with poly every
|
||||
// time a 0x100 bit appears.
|
||||
const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // x⁸ + x⁴ + x² + x + 1
|
||||
const poly = 1<<8 | 1<<4 | 1<<3 | 1<<1 | 1<<0 // x⁸ + x⁴ + x³ + x + 1
|
||||
|
||||
// Powers of x mod poly in GF(2).
|
||||
var powx = [16]byte{
|
||||
|
|
|
@ -66,7 +66,9 @@ func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)
|
|||
// hashToInt converts a hash value to an integer. There is some disagreement
|
||||
// about how this is done. [NSA] suggests that this is done in the obvious
|
||||
// manner, but [SECG] truncates the hash to the bit-length of the curve order
|
||||
// first. We follow [SECG] because that's what OpenSSL does.
|
||||
// first. We follow [SECG] because that's what OpenSSL does. Additionally,
|
||||
// OpenSSL right shifts excess bits from the number if the hash is too large
|
||||
// and we mirror that too.
|
||||
func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
|
||||
orderBits := c.Params().N.BitLen()
|
||||
orderBytes := (orderBits + 7) / 8
|
||||
|
@ -75,7 +77,7 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
|
|||
}
|
||||
|
||||
ret := new(big.Int).SetBytes(hash)
|
||||
excess := orderBytes*8 - orderBits
|
||||
excess := len(hash)*8 - orderBits
|
||||
if excess > 0 {
|
||||
ret.Rsh(ret, uint(excess))
|
||||
}
|
||||
|
|
|
@ -151,6 +151,7 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
|
|||
var hashPrefixes = map[crypto.Hash][]byte{
|
||||
crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10},
|
||||
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14},
|
||||
crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c},
|
||||
crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20},
|
||||
crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30},
|
||||
crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40},
|
||||
|
|
|
@ -563,7 +563,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
|
|||
if len(d) < 4 {
|
||||
return false
|
||||
}
|
||||
certLen := uint32(d[0])<<24 | uint32(d[1])<<8 | uint32(d[2])
|
||||
certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
|
||||
if uint32(len(d)) < 3+certLen {
|
||||
return false
|
||||
}
|
||||
|
@ -575,7 +575,7 @@ func (m *certificateMsg) unmarshal(data []byte) bool {
|
|||
m.certificates = make([][]byte, numCerts)
|
||||
d = data[7:]
|
||||
for i := 0; i < numCerts; i++ {
|
||||
certLen := uint32(d[0])<<24 | uint32(d[1])<<8 | uint32(d[2])
|
||||
certLen := uint32(d[0])<<16 | uint32(d[1])<<8 | uint32(d[2])
|
||||
m.certificates[i] = d[3 : 3+certLen]
|
||||
d = d[3+certLen:]
|
||||
}
|
||||
|
|
|
@ -388,10 +388,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature
|
|||
return ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
h := hashType.New()
|
||||
if h == nil {
|
||||
if !hashType.Available() {
|
||||
return ErrUnsupportedAlgorithm
|
||||
}
|
||||
h := hashType.New()
|
||||
|
||||
h.Write(signed)
|
||||
digest := h.Sum(nil)
|
||||
|
|
|
@ -7,14 +7,19 @@ package gosym
|
|||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var pclinetestBinary string
|
||||
var (
|
||||
pclineTempDir string
|
||||
pclinetestBinary string
|
||||
)
|
||||
|
||||
func dotest() bool {
|
||||
// For now, only works on ELF platforms.
|
||||
|
@ -24,10 +29,18 @@ func dotest() bool {
|
|||
if pclinetestBinary != "" {
|
||||
return true
|
||||
}
|
||||
var err error
|
||||
pclineTempDir, err = ioutil.TempDir("", "pclinetest")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if strings.Contains(pclineTempDir, " ") {
|
||||
panic("unexpected space in tempdir")
|
||||
}
|
||||
// This command builds pclinetest from pclinetest.asm;
|
||||
// the resulting binary looks like it was built from pclinetest.s,
|
||||
// but we have renamed it to keep it away from the go tool.
|
||||
pclinetestBinary = os.TempDir() + "/pclinetest"
|
||||
pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
|
||||
command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
|
||||
pclinetestBinary, pclinetestBinary, pclinetestBinary)
|
||||
cmd := exec.Command("sh", "-c", command)
|
||||
|
@ -170,6 +183,7 @@ func TestPCLine(t *testing.T) {
|
|||
if !dotest() {
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(pclineTempDir)
|
||||
|
||||
f, tab := crack(pclinetestBinary, t)
|
||||
text := f.Section(".text")
|
||||
|
|
|
@ -318,7 +318,7 @@ func (d *decoder) Read(p []byte) (n int, err error) {
|
|||
}
|
||||
nn, d.err = io.ReadAtLeast(d.r, d.buf[d.nbuf:nn], 4-d.nbuf)
|
||||
d.nbuf += nn
|
||||
if d.nbuf < 4 {
|
||||
if d.err != nil || d.nbuf < 4 {
|
||||
return 0, d.err
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,11 @@ package base64
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type testpair struct {
|
||||
|
@ -226,3 +228,50 @@ func TestNewLineCharacters(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type nextRead struct {
|
||||
n int // bytes to return
|
||||
err error // error to return
|
||||
}
|
||||
|
||||
// faultInjectReader returns data from source, rate-limited
|
||||
// and with the errors as written to nextc.
|
||||
type faultInjectReader struct {
|
||||
source string
|
||||
nextc <-chan nextRead
|
||||
}
|
||||
|
||||
func (r *faultInjectReader) Read(p []byte) (int, error) {
|
||||
nr := <-r.nextc
|
||||
if len(p) > nr.n {
|
||||
p = p[:nr.n]
|
||||
}
|
||||
n := copy(p, r.source)
|
||||
r.source = r.source[n:]
|
||||
return n, nr.err
|
||||
}
|
||||
|
||||
// tests that we don't ignore errors from our underlying reader
|
||||
func TestDecoderIssue3577(t *testing.T) {
|
||||
next := make(chan nextRead, 10)
|
||||
wantErr := errors.New("my error")
|
||||
next <- nextRead{5, nil}
|
||||
next <- nextRead{10, wantErr}
|
||||
d := NewDecoder(StdEncoding, &faultInjectReader{
|
||||
source: "VHdhcyBicmlsbGlnLCBhbmQgdGhlIHNsaXRoeSB0b3Zlcw==", // twas brillig...
|
||||
nextc: next,
|
||||
})
|
||||
errc := make(chan error)
|
||||
go func() {
|
||||
_, err := ioutil.ReadAll(d)
|
||||
errc <- err
|
||||
}()
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != wantErr {
|
||||
t.Errorf("got error %v; want %v", err, wantErr)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Errorf("timeout; Decoder blocked without returning an error")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ uninterpreted bytes of the value.
|
|||
All other slices and arrays are sent as an unsigned count followed by that many
|
||||
elements using the standard gob encoding for their type, recursively.
|
||||
|
||||
Maps are sent as an unsigned count followed by that man key, element
|
||||
Maps are sent as an unsigned count followed by that many key, element
|
||||
pairs. Empty but non-nil maps are sent, so if the sender has allocated
|
||||
a map, the receiver will allocate a map even no elements are
|
||||
transmitted.
|
||||
|
|
|
@ -273,10 +273,15 @@ func (d *decodeState) indirect(v reflect.Value, decodingNull bool) (Unmarshaler,
|
|||
_, isUnmarshaler = v.Interface().(Unmarshaler)
|
||||
}
|
||||
|
||||
// Load value from interface, but only if the result will be
|
||||
// usefully addressable.
|
||||
if iv := v; iv.Kind() == reflect.Interface && !iv.IsNil() {
|
||||
v = iv.Elem()
|
||||
e := iv.Elem()
|
||||
if e.Kind() == reflect.Ptr && !e.IsNil() && (!decodingNull || e.Elem().Kind() == reflect.Ptr) {
|
||||
v = e
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
pv := v
|
||||
if pv.Kind() != reflect.Ptr {
|
||||
|
@ -588,6 +593,11 @@ func (d *decodeState) literal(v reflect.Value) {
|
|||
// produce more helpful error messages.
|
||||
func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
|
||||
// Check for unmarshaler.
|
||||
if len(item) == 0 {
|
||||
//Empty string given
|
||||
d.saveError(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
|
||||
return
|
||||
}
|
||||
wantptr := item[0] == 'n' // null
|
||||
unmarshaler, pv := d.indirect(v, wantptr)
|
||||
if unmarshaler != nil {
|
||||
|
|
|
@ -638,3 +638,68 @@ func TestAnonymous(t *testing.T) {
|
|||
t.Fatal("Unmarshal: did set T.Y")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the empty string doesn't panic decoding when ,string is specified
|
||||
// Issue 3450
|
||||
func TestEmptyString(t *testing.T) {
|
||||
type T2 struct {
|
||||
Number1 int `json:",string"`
|
||||
Number2 int `json:",string"`
|
||||
}
|
||||
data := `{"Number1":"1", "Number2":""}`
|
||||
dec := NewDecoder(strings.NewReader(data))
|
||||
var t2 T2
|
||||
err := dec.Decode(&t2)
|
||||
if err == nil {
|
||||
t.Fatal("Decode: did not return error")
|
||||
}
|
||||
if t2.Number1 != 1 {
|
||||
t.Fatal("Decode: did not set Number1")
|
||||
}
|
||||
}
|
||||
|
||||
func intp(x int) *int {
|
||||
p := new(int)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func intpp(x *int) **int {
|
||||
pp := new(*int)
|
||||
*pp = x
|
||||
return pp
|
||||
}
|
||||
|
||||
var interfaceSetTests = []struct {
|
||||
pre interface{}
|
||||
json string
|
||||
post interface{}
|
||||
}{
|
||||
{"foo", `"bar"`, "bar"},
|
||||
{"foo", `2`, 2.0},
|
||||
{"foo", `true`, true},
|
||||
{"foo", `null`, nil},
|
||||
|
||||
{nil, `null`, nil},
|
||||
{new(int), `null`, nil},
|
||||
{(*int)(nil), `null`, nil},
|
||||
{new(*int), `null`, new(*int)},
|
||||
{(**int)(nil), `null`, nil},
|
||||
{intp(1), `null`, nil},
|
||||
{intpp(nil), `null`, intpp(nil)},
|
||||
{intpp(intp(1)), `null`, intpp(nil)},
|
||||
}
|
||||
|
||||
func TestInterfaceSet(t *testing.T) {
|
||||
for _, tt := range interfaceSetTests {
|
||||
b := struct{ X interface{} }{tt.pre}
|
||||
blob := `{"X":` + tt.json + `}`
|
||||
if err := Unmarshal([]byte(blob), &b); err != nil {
|
||||
t.Errorf("Unmarshal %#q: %v", blob, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(b.X, tt.post) {
|
||||
t.Errorf("Unmarshal %#q into %#v: X=%#v, want %#v", blob, tt.pre, b.X, tt.post)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ import (
|
|||
//
|
||||
// Channel, complex, and function values cannot be encoded in JSON.
|
||||
// Attempting to encode such a value causes Marshal to return
|
||||
// an InvalidTypeError.
|
||||
// an UnsupportedTypeError.
|
||||
//
|
||||
// JSON cannot represent cyclic data structures and Marshal does not
|
||||
// handle them. Passing cyclic structures to Marshal will result in
|
||||
|
@ -157,6 +157,8 @@ type Marshaler interface {
|
|||
MarshalJSON() ([]byte, error)
|
||||
}
|
||||
|
||||
// An UnsupportedTypeError is returned by Marshal when attempting
|
||||
// to encode an unsupported value type.
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
|
|
@ -620,8 +620,9 @@ func (f *FlagSet) Var(value Value, name string, usage string) {
|
|||
flag := &Flag{name, usage, value, value.String()}
|
||||
_, alreadythere := f.formal[name]
|
||||
if alreadythere {
|
||||
fmt.Fprintf(f.out(), "%s flag redefined: %s\n", f.name, name)
|
||||
panic("flag redefinition") // Happens only if flags are declared with identical names
|
||||
msg := fmt.Sprintf("%s flag redefined: %s", f.name, name)
|
||||
fmt.Fprintln(f.out(), msg)
|
||||
panic(msg) // Happens only if flags are declared with identical names
|
||||
}
|
||||
if f.formal == nil {
|
||||
f.formal = make(map[string]*Flag)
|
||||
|
|
|
@ -136,7 +136,7 @@
|
|||
Fscanf and Fscanln read from a specified io.Reader; Sscan,
|
||||
Sscanf and Sscanln read from an argument string. Scanln,
|
||||
Fscanln and Sscanln stop scanning at a newline and require that
|
||||
the items be followed by one; Sscanf, Fscanf and Sscanf require
|
||||
the items be followed by one; Scanf, Fscanf and Sscanf require
|
||||
newlines in the input to match newlines in the format; the other
|
||||
routines treat newlines as spaces.
|
||||
|
||||
|
|
|
@ -87,8 +87,12 @@ func stripTrailingWhitespace(s string) string {
|
|||
return s[0:i]
|
||||
}
|
||||
|
||||
// Text returns the text of the comment,
|
||||
// with the comment markers - //, /*, and */ - removed.
|
||||
// Text returns the text of the comment.
|
||||
// Comment markers (//, /*, and */), the first space of a line comment, and
|
||||
// leading and trailing empty lines are removed. Multiple empty lines are
|
||||
// reduced to one, and trailing space on lines is trimmed. Unless the result
|
||||
// is empty, it is newline-terminated.
|
||||
//
|
||||
func (g *CommentGroup) Text() string {
|
||||
if g == nil {
|
||||
return ""
|
||||
|
@ -104,11 +108,9 @@ func (g *CommentGroup) Text() string {
|
|||
// The parser has given us exactly the comment text.
|
||||
switch c[1] {
|
||||
case '/':
|
||||
//-style comment
|
||||
//-style comment (no newline at the end)
|
||||
c = c[2:]
|
||||
// Remove leading space after //, if there is one.
|
||||
// TODO(gri) This appears to be necessary in isolated
|
||||
// cases (bignum.RatFromString) - why?
|
||||
// strip first space - required for Example tests
|
||||
if len(c) > 0 && c[0] == ' ' {
|
||||
c = c[1:]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ast
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var comments = []struct {
|
||||
list []string
|
||||
text string
|
||||
}{
|
||||
{[]string{"//"}, ""},
|
||||
{[]string{"// "}, ""},
|
||||
{[]string{"//", "//", "// "}, ""},
|
||||
{[]string{"// foo "}, "foo\n"},
|
||||
{[]string{"//", "//", "// foo"}, "foo\n"},
|
||||
{[]string{"// foo bar "}, "foo bar\n"},
|
||||
{[]string{"// foo", "// bar"}, "foo\nbar\n"},
|
||||
{[]string{"// foo", "//", "//", "//", "// bar"}, "foo\n\nbar\n"},
|
||||
{[]string{"// foo", "/* bar */"}, "foo\n bar\n"},
|
||||
{[]string{"//", "//", "//", "// foo", "//", "//", "//"}, "foo\n"},
|
||||
|
||||
{[]string{"/**/"}, ""},
|
||||
{[]string{"/* */"}, ""},
|
||||
{[]string{"/**/", "/**/", "/* */"}, ""},
|
||||
{[]string{"/* Foo */"}, " Foo\n"},
|
||||
{[]string{"/* Foo Bar */"}, " Foo Bar\n"},
|
||||
{[]string{"/* Foo*/", "/* Bar*/"}, " Foo\n Bar\n"},
|
||||
{[]string{"/* Foo*/", "/**/", "/**/", "/**/", "// Bar"}, " Foo\n\nBar\n"},
|
||||
{[]string{"/* Foo*/", "/*\n*/", "//", "/*\n*/", "// Bar"}, " Foo\n\nBar\n"},
|
||||
{[]string{"/* Foo*/", "// Bar"}, " Foo\nBar\n"},
|
||||
{[]string{"/* Foo\n Bar*/"}, " Foo\n Bar\n"},
|
||||
}
|
||||
|
||||
func TestCommentText(t *testing.T) {
|
||||
for i, c := range comments {
|
||||
list := make([]*Comment, len(c.list))
|
||||
for i, s := range c.list {
|
||||
list[i] = &Comment{Text: s}
|
||||
}
|
||||
|
||||
text := (&CommentGroup{list}).Text()
|
||||
if text != c.text {
|
||||
t.Errorf("case %d: got %q; expected %q", i, text, c.text)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,7 +68,7 @@ type Context struct {
|
|||
|
||||
// ReadDir returns a slice of os.FileInfo, sorted by Name,
|
||||
// describing the content of the named directory.
|
||||
// If ReadDir is nil, Import uses io.ReadDir.
|
||||
// If ReadDir is nil, Import uses ioutil.ReadDir.
|
||||
ReadDir func(dir string) (fi []os.FileInfo, err error)
|
||||
|
||||
// OpenFile opens a file (not a directory) for reading.
|
||||
|
@ -339,7 +339,7 @@ func (e *NoGoError) Error() string {
|
|||
// - files starting with _ or . (likely editor temporary files)
|
||||
// - files with build constraints not satisfied by the context
|
||||
//
|
||||
// If an error occurs, Import returns a non-nil error also returns a non-nil
|
||||
// If an error occurs, Import returns a non-nil error and a non-nil
|
||||
// *Package containing partial information.
|
||||
//
|
||||
func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
|
||||
|
|
|
@ -267,13 +267,13 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
|
|||
|
||||
// Consume a group of adjacent comments, add it to the parser's
|
||||
// comments list, and return it together with the line at which
|
||||
// the last comment in the group ends. An empty line or non-comment
|
||||
// token terminates a comment group.
|
||||
// the last comment in the group ends. A non-comment token or n
|
||||
// empty lines terminate a comment group.
|
||||
//
|
||||
func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int) {
|
||||
func (p *parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) {
|
||||
var list []*ast.Comment
|
||||
endline = p.file.Line(p.pos)
|
||||
for p.tok == token.COMMENT && endline+1 >= p.file.Line(p.pos) {
|
||||
for p.tok == token.COMMENT && p.file.Line(p.pos) <= endline+n {
|
||||
var comment *ast.Comment
|
||||
comment, endline = p.consumeComment()
|
||||
list = append(list, comment)
|
||||
|
@ -314,7 +314,7 @@ func (p *parser) next() {
|
|||
if p.file.Line(p.pos) == line {
|
||||
// The comment is on same line as the previous token; it
|
||||
// cannot be a lead comment but may be a line comment.
|
||||
comment, endline = p.consumeCommentGroup()
|
||||
comment, endline = p.consumeCommentGroup(0)
|
||||
if p.file.Line(p.pos) != endline {
|
||||
// The next token is on a different line, thus
|
||||
// the last comment group is a line comment.
|
||||
|
@ -325,7 +325,7 @@ func (p *parser) next() {
|
|||
// consume successor comments, if any
|
||||
endline = -1
|
||||
for p.tok == token.COMMENT {
|
||||
comment, endline = p.consumeCommentGroup()
|
||||
comment, endline = p.consumeCommentGroup(1)
|
||||
}
|
||||
|
||||
if endline+1 == p.file.Line(p.pos) {
|
||||
|
@ -627,10 +627,10 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
|
|||
|
||||
doc := p.leadComment
|
||||
|
||||
// fields
|
||||
// FieldDecl
|
||||
list, typ := p.parseVarList(false)
|
||||
|
||||
// optional tag
|
||||
// Tag
|
||||
var tag *ast.BasicLit
|
||||
if p.tok == token.STRING {
|
||||
tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
|
||||
|
@ -645,7 +645,6 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
|
|||
} else {
|
||||
// ["*"] TypeName (AnonymousField)
|
||||
typ = list[0] // we always have at least one element
|
||||
p.resolve(typ)
|
||||
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
|
||||
pos := typ.Pos()
|
||||
p.errorExpected(pos, "anonymous field")
|
||||
|
@ -657,6 +656,7 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
|
|||
|
||||
field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment}
|
||||
p.declare(field, nil, scope, ast.Var, idents...)
|
||||
p.resolve(typ)
|
||||
|
||||
return field
|
||||
}
|
||||
|
@ -699,12 +699,15 @@ func (p *parser) parsePointerType() *ast.StarExpr {
|
|||
return &ast.StarExpr{Star: star, X: base}
|
||||
}
|
||||
|
||||
// If the result is an identifier, it is not resolved.
|
||||
func (p *parser) tryVarType(isParam bool) ast.Expr {
|
||||
if isParam && p.tok == token.ELLIPSIS {
|
||||
pos := p.pos
|
||||
p.next()
|
||||
typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
|
||||
if typ == nil {
|
||||
if typ != nil {
|
||||
p.resolve(typ)
|
||||
} else {
|
||||
p.error(pos, "'...' parameter is missing type")
|
||||
typ = &ast.BadExpr{From: pos, To: p.pos}
|
||||
}
|
||||
|
@ -713,6 +716,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
|
|||
return p.tryIdentOrType(false)
|
||||
}
|
||||
|
||||
// If the result is an identifier, it is not resolved.
|
||||
func (p *parser) parseVarType(isParam bool) ast.Expr {
|
||||
typ := p.tryVarType(isParam)
|
||||
if typ == nil {
|
||||
|
@ -724,6 +728,7 @@ func (p *parser) parseVarType(isParam bool) ast.Expr {
|
|||
return typ
|
||||
}
|
||||
|
||||
// If any of the results are identifiers, they are not resolved.
|
||||
func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
|
||||
if p.trace {
|
||||
defer un(trace(p, "VarList"))
|
||||
|
@ -744,9 +749,7 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
|
|||
}
|
||||
|
||||
// if we had a list of identifiers, it must be followed by a type
|
||||
if typ = p.tryVarType(isParam); typ != nil {
|
||||
p.resolve(typ)
|
||||
}
|
||||
typ = p.tryVarType(isParam)
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -756,7 +759,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
|
|||
defer un(trace(p, "ParameterList"))
|
||||
}
|
||||
|
||||
// ParameterDecl
|
||||
list, typ := p.parseVarList(ellipsisOk)
|
||||
|
||||
// analyze case
|
||||
if typ != nil {
|
||||
// IdentifierList Type
|
||||
idents := p.makeIdentList(list)
|
||||
|
@ -765,10 +771,10 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
|
|||
// Go spec: The scope of an identifier denoting a function
|
||||
// parameter or result variable is the function body.
|
||||
p.declare(field, nil, scope, ast.Var, idents...)
|
||||
p.resolve(typ)
|
||||
if p.tok == token.COMMA {
|
||||
p.next()
|
||||
}
|
||||
|
||||
for p.tok != token.RPAREN && p.tok != token.EOF {
|
||||
idents := p.parseIdentList()
|
||||
typ := p.parseVarType(ellipsisOk)
|
||||
|
@ -777,18 +783,18 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
|
|||
// Go spec: The scope of an identifier denoting a function
|
||||
// parameter or result variable is the function body.
|
||||
p.declare(field, nil, scope, ast.Var, idents...)
|
||||
p.resolve(typ)
|
||||
if !p.atComma("parameter list") {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
|
||||
} else {
|
||||
// Type { "," Type } (anonymous parameters)
|
||||
params = make([]*ast.Field, len(list))
|
||||
for i, x := range list {
|
||||
p.resolve(x)
|
||||
params[i] = &ast.Field{Type: x}
|
||||
for i, typ := range list {
|
||||
p.resolve(typ)
|
||||
params[i] = &ast.Field{Type: typ}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -25,7 +27,7 @@ func TestParse(t *testing.T) {
|
|||
for _, filename := range validFiles {
|
||||
_, err := ParseFile(fset, filename, nil, DeclarationErrors)
|
||||
if err != nil {
|
||||
t.Errorf("ParseFile(%s): %v", filename, err)
|
||||
t.Fatalf("ParseFile(%s): %v", filename, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +72,7 @@ func TestParseExpr(t *testing.T) {
|
|||
src := "a + b"
|
||||
x, err := ParseExpr(src)
|
||||
if err != nil {
|
||||
t.Errorf("ParseExpr(%s): %v", src, err)
|
||||
t.Fatalf("ParseExpr(%s): %v", src, err)
|
||||
}
|
||||
// sanity check
|
||||
if _, ok := x.(*ast.BinaryExpr); !ok {
|
||||
|
@ -81,7 +83,7 @@ func TestParseExpr(t *testing.T) {
|
|||
src = "a + *"
|
||||
_, err = ParseExpr(src)
|
||||
if err == nil {
|
||||
t.Errorf("ParseExpr(%s): %v", src, err)
|
||||
t.Fatalf("ParseExpr(%s): %v", src, err)
|
||||
}
|
||||
|
||||
// it must not crash
|
||||
|
@ -93,7 +95,7 @@ func TestParseExpr(t *testing.T) {
|
|||
func TestColonEqualsScope(t *testing.T) {
|
||||
f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0)
|
||||
if err != nil {
|
||||
t.Errorf("parse: %s", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// RHS refers to undefined globals; LHS does not.
|
||||
|
@ -115,7 +117,7 @@ func TestColonEqualsScope(t *testing.T) {
|
|||
func TestVarScope(t *testing.T) {
|
||||
f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0)
|
||||
if err != nil {
|
||||
t.Errorf("parse: %s", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// RHS refers to undefined globals; LHS does not.
|
||||
|
@ -133,6 +135,67 @@ func TestVarScope(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestUnresolved(t *testing.T) {
|
||||
f, err := ParseFile(fset, "", `
|
||||
package p
|
||||
//
|
||||
func f1a(int)
|
||||
func f2a(byte, int, float)
|
||||
func f3a(a, b int, c float)
|
||||
func f4a(...complex)
|
||||
func f5a(a s1a, b ...complex)
|
||||
//
|
||||
func f1b(*int)
|
||||
func f2b([]byte, (int), *float)
|
||||
func f3b(a, b *int, c []float)
|
||||
func f4b(...*complex)
|
||||
func f5b(a s1a, b ...[]complex)
|
||||
//
|
||||
type s1a struct { int }
|
||||
type s2a struct { byte; int; s1a }
|
||||
type s3a struct { a, b int; c float }
|
||||
//
|
||||
type s1b struct { *int }
|
||||
type s2b struct { byte; int; *float }
|
||||
type s3b struct { a, b *s3b; c []float }
|
||||
`, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := "int " + // f1a
|
||||
"byte int float " + // f2a
|
||||
"int float " + // f3a
|
||||
"complex " + // f4a
|
||||
"complex " + // f5a
|
||||
//
|
||||
"int " + // f1b
|
||||
"byte int float " + // f2b
|
||||
"int float " + // f3b
|
||||
"complex " + // f4b
|
||||
"complex " + // f5b
|
||||
//
|
||||
"int " + // s1a
|
||||
"byte int " + // s2a
|
||||
"int float " + // s3a
|
||||
//
|
||||
"int " + // s1a
|
||||
"byte int float " + // s2a
|
||||
"float " // s3a
|
||||
|
||||
// collect unresolved identifiers
|
||||
var buf bytes.Buffer
|
||||
for _, u := range f.Unresolved {
|
||||
buf.WriteString(u.Name)
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
got := buf.String()
|
||||
|
||||
if got != want {
|
||||
t.Errorf("\ngot: %s\nwant: %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
var imports = map[string]bool{
|
||||
`"a"`: true,
|
||||
"`a`": true,
|
||||
|
@ -177,3 +240,125 @@ func TestImports(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommentGroups(t *testing.T) {
|
||||
f, err := ParseFile(fset, "", `
|
||||
package p /* 1a */ /* 1b */ /* 1c */ // 1d
|
||||
/* 2a
|
||||
*/
|
||||
// 2b
|
||||
const pi = 3.1415
|
||||
/* 3a */ // 3b
|
||||
/* 3c */ const e = 2.7182
|
||||
|
||||
// Example from issue 3139
|
||||
func ExampleCount() {
|
||||
fmt.Println(strings.Count("cheese", "e"))
|
||||
fmt.Println(strings.Count("five", "")) // before & after each rune
|
||||
// Output:
|
||||
// 3
|
||||
// 5
|
||||
}
|
||||
`, ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expected := [][]string{
|
||||
{"/* 1a */", "/* 1b */", "/* 1c */", "// 1d"},
|
||||
{"/* 2a\n*/", "// 2b"},
|
||||
{"/* 3a */", "// 3b", "/* 3c */"},
|
||||
{"// Example from issue 3139"},
|
||||
{"// before & after each rune"},
|
||||
{"// Output:", "// 3", "// 5"},
|
||||
}
|
||||
if len(f.Comments) != len(expected) {
|
||||
t.Fatalf("got %d comment groups; expected %d", len(f.Comments), len(expected))
|
||||
}
|
||||
for i, exp := range expected {
|
||||
got := f.Comments[i].List
|
||||
if len(got) != len(exp) {
|
||||
t.Errorf("got %d comments in group %d; expected %d", len(got), i, len(exp))
|
||||
continue
|
||||
}
|
||||
for j, exp := range exp {
|
||||
got := got[j].Text
|
||||
if got != exp {
|
||||
t.Errorf("got %q in group %d; expected %q", got, i, exp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getField(file *ast.File, fieldname string) *ast.Field {
|
||||
parts := strings.Split(fieldname, ".")
|
||||
for _, d := range file.Decls {
|
||||
if d, ok := d.(*ast.GenDecl); ok && d.Tok == token.TYPE {
|
||||
for _, s := range d.Specs {
|
||||
if s, ok := s.(*ast.TypeSpec); ok && s.Name.Name == parts[0] {
|
||||
if s, ok := s.Type.(*ast.StructType); ok {
|
||||
for _, f := range s.Fields.List {
|
||||
for _, name := range f.Names {
|
||||
if name.Name == parts[1] {
|
||||
return f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Don't use ast.CommentGroup.Text() - we want to see exact comment text.
|
||||
func commentText(c *ast.CommentGroup) string {
|
||||
var buf bytes.Buffer
|
||||
if c != nil {
|
||||
for _, c := range c.List {
|
||||
buf.WriteString(c.Text)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line string) {
|
||||
f := getField(file, fieldname)
|
||||
if f == nil {
|
||||
t.Fatalf("field not found: %s", fieldname)
|
||||
}
|
||||
if got := commentText(f.Doc); got != lead {
|
||||
t.Errorf("got lead comment %q; expected %q", got, lead)
|
||||
}
|
||||
if got := commentText(f.Comment); got != line {
|
||||
t.Errorf("got line comment %q; expected %q", got, line)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLeadAndLineComments(t *testing.T) {
|
||||
f, err := ParseFile(fset, "", `
|
||||
package p
|
||||
type T struct {
|
||||
/* F1 lead comment */
|
||||
//
|
||||
F1 int /* F1 */ // line comment
|
||||
// F2 lead
|
||||
// comment
|
||||
F2 int // F2 line comment
|
||||
// f3 lead comment
|
||||
f3 int // f3 line comment
|
||||
}
|
||||
`, ParseComments)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
|
||||
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
|
||||
checkFieldComments(t, f, "T.f3", "// f3 lead comment", "// f3 line comment")
|
||||
ast.FileExports(f)
|
||||
checkFieldComments(t, f, "T.F1", "/* F1 lead comment *///", "/* F1 */// line comment")
|
||||
checkFieldComments(t, f, "T.F2", "// F2 lead// comment", "// F2 line comment")
|
||||
if getField(f, "T.f3") != nil {
|
||||
t.Error("not expected to find T.f3")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,8 +60,8 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (prin
|
|||
|
||||
// setComment sets g as the next comment if g != nil and if node comments
|
||||
// are enabled - this mode is used when printing source code fragments such
|
||||
// as exports only. It assumes that there are no other pending comments to
|
||||
// intersperse.
|
||||
// as exports only. It assumes that there is no pending comment in p.comments
|
||||
// and at most one pending comment in the p.comment cache.
|
||||
func (p *printer) setComment(g *ast.CommentGroup) {
|
||||
if g == nil || !p.useNodeComments {
|
||||
return
|
||||
|
@ -74,11 +74,20 @@ func (p *printer) setComment(g *ast.CommentGroup) {
|
|||
// should never happen - handle gracefully and flush
|
||||
// all comments up to g, ignore anything after that
|
||||
p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
|
||||
p.comments = p.comments[0:1]
|
||||
// in debug mode, report error
|
||||
p.internalError("setComment found pending comments")
|
||||
}
|
||||
p.comments[0] = g
|
||||
p.cindex = 0
|
||||
// don't overwrite any pending comment in the p.comment cache
|
||||
// (there may be a pending comment when a line comment is
|
||||
// immediately followed by a lead comment with no other
|
||||
// tokens inbetween)
|
||||
if p.commentOffset == infinity {
|
||||
p.nextComment() // get comment ready for use
|
||||
}
|
||||
}
|
||||
|
||||
type exprListMode uint
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ const (
|
|||
// Bits or'ed together to control what's printed. There is no control over the
|
||||
// order they appear (the order listed here) or the format they present (as
|
||||
// described in the comments). A colon appears after these items:
|
||||
// 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
|
||||
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
|
||||
Ldate = 1 << iota // the date: 2009/01/23
|
||||
Ltime // the time: 01:23:23
|
||||
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
|
||||
|
|
|
@ -271,10 +271,10 @@ func karatsuba(z, x, y nat) {
|
|||
// xd = x1 - x0
|
||||
// yd = y0 - y1
|
||||
//
|
||||
// z1 = xd*yd + z1 + z0
|
||||
// = (x1-x0)*(y0 - y1) + z1 + z0
|
||||
// = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z1 + z0
|
||||
// = x1*y0 - z1 - z0 + x0*y1 + z1 + z0
|
||||
// z1 = xd*yd + z2 + z0
|
||||
// = (x1-x0)*(y0 - y1) + z2 + z0
|
||||
// = x1*y0 - x1*y1 - x0*y0 + x0*y1 + z2 + z0
|
||||
// = x1*y0 - z2 - z0 + x0*y1 + z2 + z0
|
||||
// = x1*y0 + x0*y1
|
||||
|
||||
// split x, y into "digits"
|
||||
|
@ -318,7 +318,7 @@ func karatsuba(z, x, y nat) {
|
|||
// save original z2:z0
|
||||
// (ok to use upper half of z since we're done recursing)
|
||||
r := z[n*4:]
|
||||
copy(r, z)
|
||||
copy(r, z[:n*2])
|
||||
|
||||
// add up all partial products
|
||||
//
|
||||
|
|
|
@ -661,3 +661,21 @@ func TestExpNN(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ExpHelper(b *testing.B, x, y Word) {
|
||||
var z nat
|
||||
for i := 0; i < b.N; i++ {
|
||||
z.expWW(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExp3Power0x10(b *testing.B) { ExpHelper(b, 3, 0x10) }
|
||||
func BenchmarkExp3Power0x40(b *testing.B) { ExpHelper(b, 3, 0x40) }
|
||||
func BenchmarkExp3Power0x100(b *testing.B) { ExpHelper(b, 3, 0x100) }
|
||||
func BenchmarkExp3Power0x400(b *testing.B) { ExpHelper(b, 3, 0x400) }
|
||||
func BenchmarkExp3Power0x1000(b *testing.B) { ExpHelper(b, 3, 0x1000) }
|
||||
func BenchmarkExp3Power0x4000(b *testing.B) { ExpHelper(b, 3, 0x4000) }
|
||||
func BenchmarkExp3Power0x10000(b *testing.B) { ExpHelper(b, 3, 0x10000) }
|
||||
func BenchmarkExp3Power0x40000(b *testing.B) { ExpHelper(b, 3, 0x40000) }
|
||||
func BenchmarkExp3Power0x100000(b *testing.B) { ExpHelper(b, 3, 0x100000) }
|
||||
func BenchmarkExp3Power0x400000(b *testing.B) { ExpHelper(b, 3, 0x400000) }
|
||||
|
|
|
@ -22,11 +22,6 @@ import (
|
|||
"net/textproto"
|
||||
)
|
||||
|
||||
// TODO(bradfitz): inline these once the compiler can inline them in
|
||||
// read-only situation (such as bytes.HasSuffix)
|
||||
var lf = []byte("\n")
|
||||
var crlf = []byte("\r\n")
|
||||
|
||||
var emptyParams = make(map[string]string)
|
||||
|
||||
// A Part represents a single part in a multipart body.
|
||||
|
@ -38,6 +33,7 @@ type Part struct {
|
|||
|
||||
buffer *bytes.Buffer
|
||||
mr *Reader
|
||||
bytesRead int
|
||||
|
||||
disposition string
|
||||
dispositionParams map[string]string
|
||||
|
@ -113,14 +109,26 @@ func (bp *Part) populateHeaders() error {
|
|||
// Read reads the body of a part, after its headers and before the
|
||||
// next part (if any) begins.
|
||||
func (p *Part) Read(d []byte) (n int, err error) {
|
||||
defer func() {
|
||||
p.bytesRead += n
|
||||
}()
|
||||
if p.buffer.Len() >= len(d) {
|
||||
// Internal buffer of unconsumed data is large enough for
|
||||
// the read request. No need to parse more at the moment.
|
||||
return p.buffer.Read(d)
|
||||
}
|
||||
peek, err := p.mr.bufReader.Peek(4096) // TODO(bradfitz): add buffer size accessor
|
||||
unexpectedEof := err == io.EOF
|
||||
if err != nil && !unexpectedEof {
|
||||
|
||||
// Look for an immediate empty part without a leading \r\n
|
||||
// before the boundary separator. Some MIME code makes empty
|
||||
// parts like this. Most browsers, however, write the \r\n
|
||||
// before the subsequent boundary even for empty parts and
|
||||
// won't hit this path.
|
||||
if p.bytesRead == 0 && p.mr.peekBufferIsEmptyPart(peek) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
unexpectedEOF := err == io.EOF
|
||||
if err != nil && !unexpectedEOF {
|
||||
return 0, fmt.Errorf("multipart: Part Read: %v", err)
|
||||
}
|
||||
if peek == nil {
|
||||
|
@ -138,7 +146,7 @@ func (p *Part) Read(d []byte) (n int, err error) {
|
|||
foundBoundary = true
|
||||
} else if safeCount := len(peek) - len(p.mr.nlDashBoundary); safeCount > 0 {
|
||||
nCopy = safeCount
|
||||
} else if unexpectedEof {
|
||||
} else if unexpectedEOF {
|
||||
// If we've run out of peek buffer and the boundary
|
||||
// wasn't found (and can't possibly fit), we must have
|
||||
// hit the end of the file unexpectedly.
|
||||
|
@ -172,7 +180,10 @@ type Reader struct {
|
|||
currentPart *Part
|
||||
partsRead int
|
||||
|
||||
nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
|
||||
nl []byte // "\r\n" or "\n" (set after seeing first boundary line)
|
||||
nlDashBoundary []byte // nl + "--boundary"
|
||||
dashBoundaryDash []byte // "--boundary--"
|
||||
dashBoundary []byte // "--boundary"
|
||||
}
|
||||
|
||||
// NextPart returns the next part in the multipart or an error.
|
||||
|
@ -185,7 +196,7 @@ func (r *Reader) NextPart() (*Part, error) {
|
|||
expectNewPart := false
|
||||
for {
|
||||
line, err := r.bufReader.ReadSlice('\n')
|
||||
if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) {
|
||||
if err == io.EOF && r.isFinalBoundary(line) {
|
||||
// If the buffer ends in "--boundary--" without the
|
||||
// trailing "\r\n", ReadSlice will return an error
|
||||
// (since it's missing the '\n'), but this is a valid
|
||||
|
@ -207,7 +218,7 @@ func (r *Reader) NextPart() (*Part, error) {
|
|||
return bp, nil
|
||||
}
|
||||
|
||||
if hasPrefixThenNewline(line, r.dashBoundaryDash) {
|
||||
if r.isFinalBoundary(line) {
|
||||
// Expected EOF
|
||||
return nil, io.EOF
|
||||
}
|
||||
|
@ -235,7 +246,19 @@ func (r *Reader) NextPart() (*Part, error) {
|
|||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
|
||||
// isFinalBoundary returns whether line is the final boundary line
|
||||
// indiciating that all parts are over.
|
||||
// It matches `^--boundary--[ \t]*(\r\n)?$`
|
||||
func (mr *Reader) isFinalBoundary(line []byte) bool {
|
||||
if !bytes.HasPrefix(line, mr.dashBoundaryDash) {
|
||||
return false
|
||||
}
|
||||
rest := line[len(mr.dashBoundaryDash):]
|
||||
rest = skipLWSPChar(rest)
|
||||
return len(rest) == 0 || bytes.Equal(rest, mr.nl)
|
||||
}
|
||||
|
||||
func (mr *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
|
||||
// http://tools.ietf.org/html/rfc2046#section-5.1
|
||||
// The boundary delimiter line is then defined as a line
|
||||
// consisting entirely of two hyphen characters ("-",
|
||||
|
@ -245,32 +268,52 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
|
|||
if !bytes.HasPrefix(line, mr.dashBoundary) {
|
||||
return false
|
||||
}
|
||||
if bytes.HasSuffix(line, mr.nl) {
|
||||
return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
|
||||
}
|
||||
// Violate the spec and also support newlines without the
|
||||
// carriage return...
|
||||
if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
|
||||
if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
|
||||
rest := line[len(mr.dashBoundary):]
|
||||
rest = skipLWSPChar(rest)
|
||||
|
||||
// On the first part, see our lines are ending in \n instead of \r\n
|
||||
// and switch into that mode if so. This is a violation of the spec,
|
||||
// but occurs in practice.
|
||||
if mr.partsRead == 0 && len(rest) == 1 && rest[0] == '\n' {
|
||||
mr.nl = mr.nl[1:]
|
||||
mr.nlDashBoundary = mr.nlDashBoundary[1:]
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return bytes.Equal(rest, mr.nl)
|
||||
}
|
||||
|
||||
func onlyHorizontalWhitespace(s []byte) bool {
|
||||
for _, b := range s {
|
||||
if b != ' ' && b != '\t' {
|
||||
// peekBufferIsEmptyPart returns whether the provided peek-ahead
|
||||
// buffer represents an empty part. This is only called if we've not
|
||||
// already read any bytes in this part and checks for the case of MIME
|
||||
// software not writing the \r\n on empty parts. Some does, some
|
||||
// doesn't.
|
||||
//
|
||||
// This checks that what follows the "--boundary" is actually the end
|
||||
// ("--boundary--" with optional whitespace) or optional whitespace
|
||||
// and then a newline, so we don't catch "--boundaryFAKE", in which
|
||||
// case the whole line is part of the data.
|
||||
func (mr *Reader) peekBufferIsEmptyPart(peek []byte) bool {
|
||||
// End of parts case.
|
||||
// Test whether peek matches `^--boundary--[ \t]*(?:\r\n|$)`
|
||||
if bytes.HasPrefix(peek, mr.dashBoundaryDash) {
|
||||
rest := peek[len(mr.dashBoundaryDash):]
|
||||
rest = skipLWSPChar(rest)
|
||||
return bytes.HasPrefix(rest, mr.nl) || len(rest) == 0
|
||||
}
|
||||
if !bytes.HasPrefix(peek, mr.dashBoundary) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
// Test whether rest matches `^[ \t]*\r\n`)
|
||||
rest := peek[len(mr.dashBoundary):]
|
||||
rest = skipLWSPChar(rest)
|
||||
return bytes.HasPrefix(rest, mr.nl)
|
||||
}
|
||||
|
||||
func hasPrefixThenNewline(s, prefix []byte) bool {
|
||||
return bytes.HasPrefix(s, prefix) &&
|
||||
(len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
|
||||
len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
|
||||
// skipLWSPChar returns b with leading spaces and tabs removed.
|
||||
// RFC 822 defines:
|
||||
// LWSP-char = SPACE / HTAB
|
||||
func skipLWSPChar(b []byte) []byte {
|
||||
for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
|
||||
b = b[1:]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -10,20 +10,13 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHorizontalWhitespace(t *testing.T) {
|
||||
if !onlyHorizontalWhitespace([]byte(" \t")) {
|
||||
t.Error("expected pass")
|
||||
}
|
||||
if onlyHorizontalWhitespace([]byte("foo bar")) {
|
||||
t.Error("expected failure")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundaryLine(t *testing.T) {
|
||||
mr := NewReader(strings.NewReader(""), "myBoundary")
|
||||
if !mr.isBoundaryDelimiterLine([]byte("--myBoundary\r\n")) {
|
||||
|
@ -319,29 +312,6 @@ Oh no, premature EOF!
|
|||
}
|
||||
}
|
||||
|
||||
func TestZeroLengthBody(t *testing.T) {
|
||||
testBody := strings.Replace(`
|
||||
This is a multi-part message. This line is ignored.
|
||||
--MyBoundary
|
||||
foo: bar
|
||||
|
||||
|
||||
--MyBoundary--
|
||||
`, "\n", "\r\n", -1)
|
||||
r := NewReader(strings.NewReader(testBody), "MyBoundary")
|
||||
part, err := r.NextPart()
|
||||
if err != nil {
|
||||
t.Fatalf("didn't get a part")
|
||||
}
|
||||
n, err := io.Copy(ioutil.Discard, part)
|
||||
if err != nil {
|
||||
t.Errorf("error reading part: %v", err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Errorf("read %d bytes; expected 0", n)
|
||||
}
|
||||
}
|
||||
|
||||
type slowReader struct {
|
||||
r io.Reader
|
||||
}
|
||||
|
@ -427,3 +397,214 @@ func TestNested(t *testing.T) {
|
|||
t.Fatalf("final outer NextPart = %v; want io.EOF", err)
|
||||
}
|
||||
}
|
||||
|
||||
type headerBody struct {
|
||||
header textproto.MIMEHeader
|
||||
body string
|
||||
}
|
||||
|
||||
func formData(key, value string) headerBody {
|
||||
return headerBody{
|
||||
textproto.MIMEHeader{
|
||||
"Content-Type": {"text/plain; charset=ISO-8859-1"},
|
||||
"Content-Disposition": {"form-data; name=" + key},
|
||||
},
|
||||
value,
|
||||
}
|
||||
}
|
||||
|
||||
type parseTest struct {
|
||||
name string
|
||||
in, sep string
|
||||
want []headerBody
|
||||
}
|
||||
|
||||
var parseTests = []parseTest{
|
||||
// Actual body from App Engine on a blob upload. The final part (the
|
||||
// Content-Type: message/external-body) is what App Engine replaces
|
||||
// the uploaded file with. The other form fields (prefixed with
|
||||
// "other" in their form-data name) are unchanged. A bug was
|
||||
// reported with blob uploads failing when the other fields were
|
||||
// empty. This was the MIME POST body that previously failed.
|
||||
{
|
||||
name: "App Engine post",
|
||||
sep: "00151757727e9583fd04bfbca4c6",
|
||||
in: "--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty1\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo1\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherFoo2\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherEmpty2\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatFoo\r\n\r\nfoo\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=otherRepeatEmpty\r\n\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--00151757727e9583fd04bfbca4c6\r\nContent-Type: message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\nContent-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n\r\n--00151757727e9583fd04bfbca4c6--",
|
||||
want: []headerBody{
|
||||
formData("otherEmpty1", ""),
|
||||
formData("otherFoo1", "foo"),
|
||||
formData("otherFoo2", "foo"),
|
||||
formData("otherEmpty2", ""),
|
||||
formData("otherRepeatFoo", "foo"),
|
||||
formData("otherRepeatFoo", "foo"),
|
||||
formData("otherRepeatEmpty", ""),
|
||||
formData("otherRepeatEmpty", ""),
|
||||
formData("submit", "Submit"),
|
||||
{textproto.MIMEHeader{
|
||||
"Content-Type": {"message/external-body; charset=ISO-8859-1; blob-key=AHAZQqG84qllx7HUqO_oou5EvdYQNS3Mbbkb0RjjBoM_Kc1UqEN2ygDxWiyCPulIhpHRPx-VbpB6RX4MrsqhWAi_ZxJ48O9P2cTIACbvATHvg7IgbvZytyGMpL7xO1tlIvgwcM47JNfv_tGhy1XwyEUO8oldjPqg5Q"},
|
||||
"Content-Disposition": {"form-data; name=file; filename=\"fall.png\""},
|
||||
}, "Content-Type: image/png\r\nContent-Length: 232303\r\nX-AppEngine-Upload-Creation: 2012-05-10 23:14:02.715173\r\nContent-MD5: MzRjODU1ZDZhZGU1NmRlOWEwZmMwMDdlODBmZTA0NzA=\r\nContent-Disposition: form-data; name=file; filename=\"fall.png\"\r\n\r\n"},
|
||||
},
|
||||
},
|
||||
|
||||
// Single empty part, ended with --boundary immediately after headers.
|
||||
{
|
||||
name: "single empty part, --boundary",
|
||||
sep: "abc",
|
||||
in: "--abc\r\nFoo: bar\r\n\r\n--abc--",
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
|
||||
},
|
||||
},
|
||||
|
||||
// Single empty part, ended with \r\n--boundary immediately after headers.
|
||||
{
|
||||
name: "single empty part, \r\n--boundary",
|
||||
sep: "abc",
|
||||
in: "--abc\r\nFoo: bar\r\n\r\n\r\n--abc--",
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
|
||||
},
|
||||
},
|
||||
|
||||
// Final part empty.
|
||||
{
|
||||
name: "final part empty",
|
||||
sep: "abc",
|
||||
in: "--abc\r\nFoo: bar\r\n\r\n--abc\r\nFoo2: bar2\r\n\r\n--abc--",
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
|
||||
{textproto.MIMEHeader{"Foo2": {"bar2"}}, ""},
|
||||
},
|
||||
},
|
||||
|
||||
// Final part empty with newlines after final separator.
|
||||
{
|
||||
name: "final part empty then crlf",
|
||||
sep: "abc",
|
||||
in: "--abc\r\nFoo: bar\r\n\r\n--abc--\r\n",
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
|
||||
},
|
||||
},
|
||||
|
||||
// Final part empty with lwsp-chars after final separator.
|
||||
{
|
||||
name: "final part empty then lwsp",
|
||||
sep: "abc",
|
||||
in: "--abc\r\nFoo: bar\r\n\r\n--abc-- \t",
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
|
||||
},
|
||||
},
|
||||
|
||||
// No parts (empty form as submitted by Chrome)
|
||||
{
|
||||
name: "no parts",
|
||||
sep: "----WebKitFormBoundaryQfEAfzFOiSemeHfA",
|
||||
in: "------WebKitFormBoundaryQfEAfzFOiSemeHfA--\r\n",
|
||||
want: []headerBody{},
|
||||
},
|
||||
|
||||
// Part containing data starting with the boundary, but with additional suffix.
|
||||
{
|
||||
name: "fake separator as data",
|
||||
sep: "sep",
|
||||
in: "--sep\r\nFoo: bar\r\n\r\n--sepFAKE\r\n--sep--",
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, "--sepFAKE"},
|
||||
},
|
||||
},
|
||||
|
||||
// Part containing a boundary with whitespace following it.
|
||||
{
|
||||
name: "boundary with whitespace",
|
||||
sep: "sep",
|
||||
in: "--sep \r\nFoo: bar\r\n\r\ntext\r\n--sep--",
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, "text"},
|
||||
},
|
||||
},
|
||||
|
||||
// With ignored leading line.
|
||||
{
|
||||
name: "leading line",
|
||||
sep: "MyBoundary",
|
||||
in: strings.Replace(`This is a multi-part message. This line is ignored.
|
||||
--MyBoundary
|
||||
foo: bar
|
||||
|
||||
|
||||
--MyBoundary--`, "\n", "\r\n", -1),
|
||||
want: []headerBody{
|
||||
{textproto.MIMEHeader{"Foo": {"bar"}}, ""},
|
||||
},
|
||||
},
|
||||
|
||||
roundTripParseTest(),
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
Cases:
|
||||
for _, tt := range parseTests {
|
||||
r := NewReader(strings.NewReader(tt.in), tt.sep)
|
||||
got := []headerBody{}
|
||||
for {
|
||||
p, err := r.NextPart()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("in test %q, NextPart: %v", tt.name, err)
|
||||
continue Cases
|
||||
}
|
||||
pbody, err := ioutil.ReadAll(p)
|
||||
if err != nil {
|
||||
t.Errorf("in test %q, error reading part: %v", tt.name, err)
|
||||
continue Cases
|
||||
}
|
||||
got = append(got, headerBody{p.Header, string(pbody)})
|
||||
}
|
||||
if !reflect.DeepEqual(tt.want, got) {
|
||||
t.Errorf("test %q:\n got: %v\nwant: %v", tt.name, got, tt.want)
|
||||
if len(tt.want) != len(got) {
|
||||
t.Errorf("test %q: got %d parts, want %d", tt.name, len(got), len(tt.want))
|
||||
} else if len(got) > 1 {
|
||||
for pi, wantPart := range tt.want {
|
||||
if !reflect.DeepEqual(wantPart, got[pi]) {
|
||||
t.Errorf("test %q, part %d:\n got: %v\nwant: %v", tt.name, pi, got[pi], wantPart)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func roundTripParseTest() parseTest {
|
||||
t := parseTest{
|
||||
name: "round trip",
|
||||
want: []headerBody{
|
||||
formData("empty", ""),
|
||||
formData("lf", "\n"),
|
||||
formData("cr", "\r"),
|
||||
formData("crlf", "\r\n"),
|
||||
formData("foo", "bar"),
|
||||
},
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
w := NewWriter(&buf)
|
||||
for _, p := range t.want {
|
||||
pw, err := w.CreatePart(p.header)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = pw.Write([]byte(p.body))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
w.Close()
|
||||
t.in = buf.String()
|
||||
t.sep = w.Boundary()
|
||||
return t
|
||||
}
|
||||
|
|
|
@ -89,8 +89,8 @@ func FileConn(f *os.File) (c Conn, err error) {
|
|||
|
||||
// FileListener returns a copy of the network listener corresponding
|
||||
// to the open file f. It is the caller's responsibility to close l
|
||||
// when finished. Closing c does not affect l, and closing l does not
|
||||
// affect c.
|
||||
// when finished. Closing l does not affect f, and closing f does not
|
||||
// affect l.
|
||||
func FileListener(f *os.File) (l Listener, err error) {
|
||||
fd, err := newFileFD(f)
|
||||
if err != nil {
|
||||
|
|
|
@ -278,6 +278,11 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response,
|
|||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
if c.Jar != nil {
|
||||
for _, cookie := range c.Jar.Cookies(req.URL) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
r, err = send(req, c.Transport)
|
||||
if err == nil && c.Jar != nil {
|
||||
c.Jar.SetCookies(req.URL, r.Cookies())
|
||||
|
|
|
@ -256,6 +256,31 @@ var echoCookiesRedirectHandler = HandlerFunc(func(w ResponseWriter, r *Request)
|
|||
}
|
||||
})
|
||||
|
||||
func TestClientSendsCookieFromJar(t *testing.T) {
|
||||
tr := &recordingTransport{}
|
||||
client := &Client{Transport: tr}
|
||||
client.Jar = &TestJar{perURL: make(map[string][]*Cookie)}
|
||||
us := "http://dummy.faketld/"
|
||||
u, _ := url.Parse(us)
|
||||
client.Jar.SetCookies(u, expectedCookies)
|
||||
|
||||
client.Get(us) // Note: doesn't hit network
|
||||
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
||||
|
||||
client.Head(us) // Note: doesn't hit network
|
||||
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
||||
|
||||
client.Post(us, "text/plain", strings.NewReader("body")) // Note: doesn't hit network
|
||||
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
||||
|
||||
client.PostForm(us, url.Values{}) // Note: doesn't hit network
|
||||
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
||||
|
||||
req, _ := NewRequest("GET", us, nil)
|
||||
client.Do(req) // Note: doesn't hit network
|
||||
matchReturnedCookies(t, expectedCookies, tr.req.Cookies())
|
||||
}
|
||||
|
||||
// Just enough correctness for our redirect tests. Uses the URL.Host as the
|
||||
// scope of all cookies.
|
||||
type TestJar struct {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
@ -46,3 +47,32 @@ func TestUseProxy(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cacheKeysTests = []struct {
|
||||
proxy string
|
||||
scheme string
|
||||
addr string
|
||||
key string
|
||||
}{
|
||||
{"", "http", "foo.com", "|http|foo.com"},
|
||||
{"", "https", "foo.com", "|https|foo.com"},
|
||||
{"http://foo.com", "http", "foo.com", "http://foo.com|http|"},
|
||||
{"http://foo.com", "https", "foo.com", "http://foo.com|https|foo.com"},
|
||||
}
|
||||
|
||||
func TestCacheKeys(t *testing.T) {
|
||||
for _, tt := range cacheKeysTests {
|
||||
var proxy *url.URL
|
||||
if tt.proxy != "" {
|
||||
u, err := url.Parse(tt.proxy)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxy = u
|
||||
}
|
||||
cm := connectMethod{proxy, tt.scheme, tt.addr}
|
||||
if cm.String() != tt.key {
|
||||
t.Fatalf("{%q, %q, %q} cache key %q; want %q", tt.proxy, tt.scheme, tt.addr, cm.String(), tt.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -202,9 +202,12 @@ func (r *Response) Write(w io.Writer) error {
|
|||
text = "status code " + strconv.Itoa(r.StatusCode)
|
||||
}
|
||||
}
|
||||
io.WriteString(w, "HTTP/"+strconv.Itoa(r.ProtoMajor)+".")
|
||||
io.WriteString(w, strconv.Itoa(r.ProtoMinor)+" ")
|
||||
io.WriteString(w, strconv.Itoa(r.StatusCode)+" "+text+"\r\n")
|
||||
protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
|
||||
statusCode := strconv.Itoa(r.StatusCode) + " "
|
||||
if strings.HasPrefix(text, statusCode) {
|
||||
text = text[len(statusCode):]
|
||||
}
|
||||
io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
|
||||
|
||||
// Process Body,ContentLength,Close,Trailer
|
||||
tw, err := newTransferWriter(r)
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"io/ioutil"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -444,3 +445,17 @@ func TestLocationResponse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponseStatusStutter(t *testing.T) {
|
||||
r := &Response{
|
||||
Status: "123 some status",
|
||||
StatusCode: 123,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 3,
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
r.Write(&buf)
|
||||
if strings.Contains(buf.String(), "123 123") {
|
||||
t.Errorf("stutter in status: %s", buf.String())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import (
|
|||
// Errors introduced by the HTTP server.
|
||||
var (
|
||||
ErrWriteAfterFlush = errors.New("Conn.Write called after Flush")
|
||||
ErrBodyNotAllowed = errors.New("http: response status code does not allow body")
|
||||
ErrBodyNotAllowed = errors.New("http: request method or response status code does not allow body")
|
||||
ErrHijacked = errors.New("Conn has been hijacked")
|
||||
ErrContentLength = errors.New("Conn.Write wrote more than the declared Content-Length")
|
||||
)
|
||||
|
|
|
@ -71,7 +71,9 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
|
|||
}
|
||||
}
|
||||
case *Response:
|
||||
if rr.Request != nil {
|
||||
t.Method = rr.Request.Method
|
||||
}
|
||||
t.Body = rr.Body
|
||||
t.BodyCloser = rr.Body
|
||||
t.ContentLength = rr.ContentLength
|
||||
|
@ -79,7 +81,7 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
|
|||
t.TransferEncoding = rr.TransferEncoding
|
||||
t.Trailer = rr.Trailer
|
||||
atLeastHTTP11 = rr.ProtoAtLeast(1, 1)
|
||||
t.ResponseToHEAD = noBodyExpected(rr.Request.Method)
|
||||
t.ResponseToHEAD = noBodyExpected(t.Method)
|
||||
}
|
||||
|
||||
// Sanitize Body,ContentLength,TransferEncoding
|
||||
|
|
|
@ -450,10 +450,14 @@ type connectMethod struct {
|
|||
|
||||
func (ck *connectMethod) String() string {
|
||||
proxyStr := ""
|
||||
targetAddr := ck.targetAddr
|
||||
if ck.proxyURL != nil {
|
||||
proxyStr = ck.proxyURL.String()
|
||||
if ck.targetScheme == "http" {
|
||||
targetAddr = ""
|
||||
}
|
||||
return strings.Join([]string{proxyStr, ck.targetScheme, ck.targetAddr}, "|")
|
||||
}
|
||||
return strings.Join([]string{proxyStr, ck.targetScheme, targetAddr}, "|")
|
||||
}
|
||||
|
||||
// addr returns the first hop "host:port" to which we need to TCP connect.
|
||||
|
|
|
@ -73,7 +73,8 @@ func init() {
|
|||
days := [...]string{"2", "02"} // day = 1*2DIGIT
|
||||
years := [...]string{"2006", "06"} // year = 4*DIGIT / 2*DIGIT
|
||||
seconds := [...]string{":05", ""} // second
|
||||
zones := [...]string{"-0700", "MST"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
|
||||
// "-0700 (MST)" is not in RFC 5322, but is common.
|
||||
zones := [...]string{"-0700", "MST", "-0700 (MST)"} // zone = (("+" / "-") 4DIGIT) / "GMT" / ...
|
||||
|
||||
for _, dow := range dows {
|
||||
for _, day := range days {
|
||||
|
|
|
@ -95,6 +95,11 @@ func TestDateParsing(t *testing.T) {
|
|||
"21 Nov 97 09:55:06 GMT",
|
||||
time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
|
||||
},
|
||||
// Commonly found format not specified by RFC 5322.
|
||||
{
|
||||
"Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
|
||||
time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
hdr := Header{
|
||||
|
|
|
@ -401,11 +401,12 @@ Error:
|
|||
}
|
||||
|
||||
func parseAuthority(authority string) (user *Userinfo, host string, err error) {
|
||||
if strings.Index(authority, "@") < 0 {
|
||||
i := strings.LastIndex(authority, "@")
|
||||
if i < 0 {
|
||||
host = authority
|
||||
return
|
||||
}
|
||||
userinfo, host := split(authority, '@', true)
|
||||
userinfo, host := authority[:i], authority[i+1:]
|
||||
if strings.Index(userinfo, ":") < 0 {
|
||||
if userinfo, err = unescape(userinfo, encodeUserPassword); err != nil {
|
||||
return
|
||||
|
|
|
@ -188,6 +188,37 @@ var urltests = []URLTest{
|
|||
},
|
||||
"http://user:password@google.com",
|
||||
},
|
||||
// unescaped @ in username should not confuse host
|
||||
{
|
||||
"http://j@ne:password@google.com",
|
||||
&URL{
|
||||
Scheme: "http",
|
||||
User: UserPassword("j@ne", "password"),
|
||||
Host: "google.com",
|
||||
},
|
||||
"http://j%40ne:password@google.com",
|
||||
},
|
||||
// unescaped @ in password should not confuse host
|
||||
{
|
||||
"http://jane:p@ssword@google.com",
|
||||
&URL{
|
||||
Scheme: "http",
|
||||
User: UserPassword("jane", "p@ssword"),
|
||||
Host: "google.com",
|
||||
},
|
||||
"http://jane:p%40ssword@google.com",
|
||||
},
|
||||
{
|
||||
"http://j@ne:password@google.com/p@th?q=@go",
|
||||
&URL{
|
||||
Scheme: "http",
|
||||
User: UserPassword("j@ne", "password"),
|
||||
Host: "google.com",
|
||||
Path: "/p@th",
|
||||
RawQuery: "q=@go",
|
||||
},
|
||||
"http://j%40ne:password@google.com/p@th?q=@go",
|
||||
},
|
||||
{
|
||||
"http://www.google.com/?q=go+language#foo",
|
||||
&URL{
|
||||
|
|
|
@ -204,6 +204,12 @@ func (c *Cmd) writerDescriptor(w io.Writer) (f *os.File, err error) {
|
|||
return pw, nil
|
||||
}
|
||||
|
||||
func (c *Cmd) closeDescriptors(closers []io.Closer) {
|
||||
for _, fd := range closers {
|
||||
fd.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// Run starts the specified command and waits for it to complete.
|
||||
//
|
||||
// The returned error is nil if the command runs, has no problems
|
||||
|
@ -233,6 +239,8 @@ func (c *Cmd) Start() error {
|
|||
for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
|
||||
fd, err := setupFd(c)
|
||||
if err != nil {
|
||||
c.closeDescriptors(c.closeAfterStart)
|
||||
c.closeDescriptors(c.closeAfterWait)
|
||||
return err
|
||||
}
|
||||
c.childFiles = append(c.childFiles, fd)
|
||||
|
@ -247,12 +255,12 @@ func (c *Cmd) Start() error {
|
|||
Sys: c.SysProcAttr,
|
||||
})
|
||||
if err != nil {
|
||||
c.closeDescriptors(c.closeAfterStart)
|
||||
c.closeDescriptors(c.closeAfterWait)
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fd := range c.closeAfterStart {
|
||||
fd.Close()
|
||||
}
|
||||
c.closeDescriptors(c.closeAfterStart)
|
||||
|
||||
c.errch = make(chan error, len(c.goroutine))
|
||||
for _, fn := range c.goroutine {
|
||||
|
@ -301,9 +309,7 @@ func (c *Cmd) Wait() error {
|
|||
}
|
||||
}
|
||||
|
||||
for _, fd := range c.closeAfterWait {
|
||||
fd.Close()
|
||||
}
|
||||
c.closeDescriptors(c.closeAfterWait)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -320,10 +320,13 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
|
|||
}
|
||||
|
||||
for _, fileInfo := range list {
|
||||
if err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn); err != nil {
|
||||
err = walk(Join(path, fileInfo.Name()), fileInfo, walkFn)
|
||||
if err != nil {
|
||||
if !fileInfo.IsDir() || err != SkipDir {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ func IsAbs(path string) bool {
|
|||
}
|
||||
|
||||
// VolumeName returns the leading volume name on Windows.
|
||||
// It returns "" elsewhere
|
||||
// It returns "" elsewhere.
|
||||
func VolumeName(path string) string {
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -869,3 +869,34 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
|
|||
t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup)
|
||||
}
|
||||
}
|
||||
|
||||
/* This test does not work gccgo, since the sources are arranged
|
||||
differently.
|
||||
|
||||
func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486
|
||||
root, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lib := filepath.Join(root, "lib")
|
||||
src := filepath.Join(root, "src")
|
||||
seenSrc := false
|
||||
filepath.Walk(root, func(pth string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
switch pth {
|
||||
case lib:
|
||||
return filepath.SkipDir
|
||||
case src:
|
||||
seenSrc = true
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if !seenSrc {
|
||||
t.Fatalf("%q not seen", src)
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
|
@ -512,7 +512,7 @@ func (re *Regexp) replaceAll(bsrc []byte, src string, nmatch int, repl func(dst
|
|||
}
|
||||
|
||||
// ReplaceAll returns a copy of src, replacing matches of the Regexp
|
||||
// with the replacement string repl. Inside repl, $ signs are interpreted as
|
||||
// with the replacement text repl. Inside repl, $ signs are interpreted as
|
||||
// in Expand, so for instance $1 represents the text of the first submatch.
|
||||
func (re *Regexp) ReplaceAll(src, repl []byte) []byte {
|
||||
n := 2
|
||||
|
@ -726,7 +726,7 @@ func (re *Regexp) FindSubmatch(b []byte) [][]byte {
|
|||
// the submatch with the corresponding index; other names refer to
|
||||
// capturing parentheses named with the (?P<name>...) syntax. A
|
||||
// reference to an out of range or unmatched index or a name that is not
|
||||
// present in the regular expression is replaced with an empty string.
|
||||
// present in the regular expression is replaced with an empty slice.
|
||||
//
|
||||
// In the $name form, name is taken to be as long as possible: $1x is
|
||||
// equivalent to ${1x}, not ${1}x, and, $10 is equivalent to ${10}, not ${1}0.
|
||||
|
|
|
@ -48,6 +48,9 @@ const (
|
|||
ErrTrailingBackslash ErrorCode = "trailing backslash at end of expression"
|
||||
)
|
||||
|
||||
// TODO: Export for Go 1.1.
|
||||
const errUnexpectedParen ErrorCode = "unexpected )"
|
||||
|
||||
func (e ErrorCode) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
@ -1168,13 +1171,13 @@ func (p *parser) parseRightParen() error {
|
|||
|
||||
n := len(p.stack)
|
||||
if n < 2 {
|
||||
return &Error{ErrInternalError, ""}
|
||||
return &Error{errUnexpectedParen, p.wholeRegexp}
|
||||
}
|
||||
re1 := p.stack[n-1]
|
||||
re2 := p.stack[n-2]
|
||||
p.stack = p.stack[:n-2]
|
||||
if re2.Op != opLeftParen {
|
||||
return &Error{ErrMissingParen, p.wholeRegexp}
|
||||
return &Error{errUnexpectedParen, p.wholeRegexp}
|
||||
}
|
||||
// Restore flags at time of paren.
|
||||
p.flags = re2.Flags
|
||||
|
|
|
@ -442,10 +442,18 @@ var invalidRegexps = []string{
|
|||
`(`,
|
||||
`)`,
|
||||
`(a`,
|
||||
`a)`,
|
||||
`(a))`,
|
||||
`(a|b|`,
|
||||
`a|b|)`,
|
||||
`(a|b|))`,
|
||||
`(a|b`,
|
||||
`a|b)`,
|
||||
`(a|b))`,
|
||||
`[a-z`,
|
||||
`([a-z)`,
|
||||
`[a-z)`,
|
||||
`([a-z]))`,
|
||||
`x{1001}`,
|
||||
`x{9876543210}`,
|
||||
`x{2,1}`,
|
||||
|
|
|
@ -20,7 +20,7 @@ func Goexit()
|
|||
|
||||
// Caller reports file and line number information about function invocations on
|
||||
// the calling goroutine's stack. The argument skip is the number of stack frames
|
||||
// to ascend, with 1 identifying the caller of Caller. (For historical reasons the
|
||||
// to ascend, with 0 identifying the caller of Caller. (For historical reasons the
|
||||
// meaning of skip differs between Caller and Callers.) The return values report the
|
||||
// program counter, file name, and line number within the file of the corresponding
|
||||
// call. The boolean ok is false if it was not possible to recover the information.
|
||||
|
@ -28,7 +28,8 @@ func Caller(skip int) (pc uintptr, file string, line int, ok bool)
|
|||
|
||||
// Callers fills the slice pc with the program counters of function invocations
|
||||
// on the calling goroutine's stack. The argument skip is the number of stack frames
|
||||
// to skip before recording in pc, with 0 starting at the caller of Callers.
|
||||
// to skip before recording in pc, with 0 identifying the frame for Callers itself and
|
||||
// 1 identifying the caller of Callers.
|
||||
// It returns the number of entries written to pc.
|
||||
func Callers(skip int, pc []uintptr) int
|
||||
|
||||
|
|
|
@ -4,13 +4,17 @@
|
|||
|
||||
package strconv
|
||||
|
||||
// FormatUint returns the string representation of i in the given base.
|
||||
// FormatUint returns the string representation of i in the given base,
|
||||
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
|
||||
// for digit values >= 10.
|
||||
func FormatUint(i uint64, base int) string {
|
||||
_, s := formatBits(nil, i, base, false, false)
|
||||
return s
|
||||
}
|
||||
|
||||
// FormatInt returns the string representation of i in the given base.
|
||||
// FormatInt returns the string representation of i in the given base,
|
||||
// for 2 <= base <= 36. The result uses the lower-case letters 'a' to 'z'
|
||||
// for digit values >= 10.
|
||||
func FormatInt(i int64, base int) string {
|
||||
_, s := formatBits(nil, uint64(i), base, i < 0, false)
|
||||
return s
|
||||
|
|
|
@ -41,7 +41,6 @@ func ExampleContainsAny() {
|
|||
func ExampleCount() {
|
||||
fmt.Println(strings.Count("cheese", "e"))
|
||||
fmt.Println(strings.Count("five", "")) // before & after each rune
|
||||
|
||||
// Output:
|
||||
// 3
|
||||
// 5
|
||||
|
|
|
@ -518,6 +518,13 @@ func (s *state) validateType(value reflect.Value, typ reflect.Type) reflect.Valu
|
|||
}
|
||||
}
|
||||
if !value.Type().AssignableTo(typ) {
|
||||
if value.Kind() == reflect.Interface && !value.IsNil() {
|
||||
value = value.Elem()
|
||||
if value.Type().AssignableTo(typ) {
|
||||
return value
|
||||
}
|
||||
// fallthrough
|
||||
}
|
||||
// Does one dereference or indirection work? We could do more, as we
|
||||
// do with method receivers, but that gets messy and method receivers
|
||||
// are much more constrained, so it makes more sense there than here.
|
||||
|
|
|
@ -311,6 +311,7 @@ var execTests = []execTest{
|
|||
{".VariadicFuncInt", "{{call .VariadicFuncInt 33 `he` `llo`}}", "33=<he+llo>", tVal, true},
|
||||
{"if .BinaryFunc call", "{{ if .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{end}}", "[1=2]", tVal, true},
|
||||
{"if not .BinaryFunc call", "{{ if not .BinaryFunc}}{{call .BinaryFunc `1` `2`}}{{else}}No{{end}}", "No", tVal, true},
|
||||
{"Interface Call", `{{stringer .S}}`, "foozle", map[string]interface{}{"S": bytes.NewBufferString("foozle")}, true},
|
||||
|
||||
// Erroneous function calls (check args).
|
||||
{".BinaryFuncTooFew", "{{call .BinaryFunc `1`}}", "", tVal, false},
|
||||
|
@ -507,6 +508,10 @@ func vfunc(V, *V) string {
|
|||
return "vfunc"
|
||||
}
|
||||
|
||||
func stringer(s fmt.Stringer) string {
|
||||
return s.String()
|
||||
}
|
||||
|
||||
func testExecute(execTests []execTest, template *Template, t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
funcs := FuncMap{
|
||||
|
@ -516,6 +521,7 @@ func testExecute(execTests []execTest, template *Template, t *testing.T) {
|
|||
"typeOf": typeOf,
|
||||
"vfunc": vfunc,
|
||||
"zeroArgs": zeroArgs,
|
||||
"stringer": stringer,
|
||||
}
|
||||
for _, test := range execTests {
|
||||
var tmpl *Template
|
||||
|
|
|
@ -224,3 +224,25 @@ func TestTimerStopStress(t *testing.T) {
|
|||
}
|
||||
Sleep(3 * Second)
|
||||
}
|
||||
|
||||
func TestSleepZeroDeadlock(t *testing.T) {
|
||||
// Sleep(0) used to hang, the sequence of events was as follows.
|
||||
// Sleep(0) sets G's status to Gwaiting, but then immediately returns leaving the status.
|
||||
// Then the goroutine calls e.g. new and falls down into the scheduler due to pending GC.
|
||||
// After the GC nobody wakes up the goroutine from Gwaiting status.
|
||||
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
|
||||
c := make(chan bool)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
runtime.GC()
|
||||
}
|
||||
c <- true
|
||||
}()
|
||||
for i := 0; i < 100; i++ {
|
||||
Sleep(0)
|
||||
tmp := make(chan bool, 1)
|
||||
tmp <- true
|
||||
<-tmp
|
||||
}
|
||||
<-c
|
||||
}
|
||||
|
|
|
@ -2701,7 +2701,7 @@ var _Zs = &RangeTable{
|
|||
},
|
||||
}
|
||||
|
||||
// The following variables are of type *RangeTable:
|
||||
// These variables have type *RangeTable.
|
||||
var (
|
||||
Cc = _Cc // Cc is the set of Unicode characters in category Cc.
|
||||
Cf = _Cf // Cf is the set of Unicode characters in category Cf.
|
||||
|
@ -4054,7 +4054,7 @@ var _Yi = &RangeTable{
|
|||
},
|
||||
}
|
||||
|
||||
// The following variables are of type *RangeTable:
|
||||
// These variables have type *RangeTable.
|
||||
var (
|
||||
Arabic = _Arabic // Arabic is the set of Unicode characters in script Arabic.
|
||||
Armenian = _Armenian // Armenian is the set of Unicode characters in script Armenian.
|
||||
|
@ -5116,7 +5116,7 @@ var _White_Space = &RangeTable{
|
|||
},
|
||||
}
|
||||
|
||||
// The following variables are of type *RangeTable:
|
||||
// These variables have type *RangeTable.
|
||||
var (
|
||||
ASCII_Hex_Digit = _ASCII_Hex_Digit // ASCII_Hex_Digit is the set of Unicode characters with property ASCII_Hex_Digit.
|
||||
Bidi_Control = _Bidi_Control // Bidi_Control is the set of Unicode characters with property Bidi_Control.
|
||||
|
|
|
@ -168,6 +168,7 @@ __go_free(void *v)
|
|||
c->local_by_size[sizeclass].nfree++;
|
||||
runtime_MCache_Free(c, v, sizeclass, size);
|
||||
}
|
||||
c->local_nfree++;
|
||||
c->local_alloc -= size;
|
||||
if(prof)
|
||||
runtime_MProf_Free(v, size);
|
||||
|
|
|
@ -150,8 +150,7 @@ runtime_addfinalizer(void *p, void (*f)(void*), const struct __go_func_type *ft)
|
|||
tab = TAB(p);
|
||||
runtime_lock(tab);
|
||||
if(f == nil) {
|
||||
if(lookfintab(tab, p, true, nil))
|
||||
runtime_setblockspecial(p, false);
|
||||
lookfintab(tab, p, true, nil);
|
||||
runtime_unlock(tab);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1132,7 +1132,6 @@ runfinq(void* dummy __attribute__ ((unused)))
|
|||
|
||||
f = &fb->fin[i];
|
||||
params[0] = &f->arg;
|
||||
runtime_setblockspecial(f->arg, false);
|
||||
reflect_call(f->ft, (void*)f->fn, 0, 0, params, nil);
|
||||
f->fn = nil;
|
||||
f->arg = nil;
|
||||
|
|
|
@ -61,15 +61,21 @@ ready(int64 now, Eface e)
|
|||
void
|
||||
runtime_tsleep(int64 ns)
|
||||
{
|
||||
G* g;
|
||||
Timer t;
|
||||
|
||||
if(ns <= 0)
|
||||
g = runtime_g();
|
||||
|
||||
if(ns <= 0) {
|
||||
g->status = Grunning;
|
||||
g->waitreason = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
t.when = runtime_nanotime() + ns;
|
||||
t.period = 0;
|
||||
t.f = ready;
|
||||
t.arg.__object = runtime_g();
|
||||
t.arg.__object = g;
|
||||
addtimer(&t);
|
||||
runtime_gosched();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue