diff options
| author | Filippo Valsorda <filippo@golang.org> | 2018-09-06 13:25:27 -0400 |
|---|---|---|
| committer | Filippo Valsorda <filippo@golang.org> | 2018-09-06 13:25:27 -0400 |
| commit | 4d1aa482b8754c081d8f3f6b39fe61dd2e6504fc (patch) | |
| tree | b12a76ad02035d1206d09a7aa14a6cda0c411c3c /src/encoding | |
| parent | 7eb1677c01c3decc510270d532ed69d0bf42bffa (diff) | |
| parent | 3e5b5d69dcdb82494f550049986426d84dd6b8f8 (diff) | |
| download | go-4d1aa482b8754c081d8f3f6b39fe61dd2e6504fc.tar.xz | |
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: Ia8ddd4e52dcfe87f9daef2edd37c8155fcae7f5a
Diffstat (limited to 'src/encoding')
| -rw-r--r-- | src/encoding/base64/base64.go | 66 | ||||
| -rw-r--r-- | src/encoding/binary/binary.go | 40 | ||||
| -rw-r--r-- | src/encoding/hex/hex.go | 13 | ||||
| -rw-r--r-- | src/encoding/hex/hex_test.go | 13 | ||||
| -rw-r--r-- | src/encoding/json/bench_test.go | 28 | ||||
| -rw-r--r-- | src/encoding/json/decode.go | 57 | ||||
| -rw-r--r-- | src/encoding/json/decode_test.go | 30 | ||||
| -rw-r--r-- | src/encoding/json/encode.go | 164 | ||||
| -rw-r--r-- | src/encoding/json/encode_test.go | 28 | ||||
| -rw-r--r-- | src/encoding/json/stream.go | 24 | ||||
| -rw-r--r-- | src/encoding/json/stream_test.go | 34 | ||||
| -rw-r--r-- | src/encoding/pem/pem_test.go | 4 | ||||
| -rw-r--r-- | src/encoding/xml/xml.go | 14 |
13 files changed, 317 insertions, 198 deletions
diff --git a/src/encoding/base64/base64.go b/src/encoding/base64/base64.go index 9a99370f1e..e8afc48859 100644 --- a/src/encoding/base64/base64.go +++ b/src/encoding/base64/base64.go @@ -465,10 +465,9 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { } si := 0 - ilen := len(src) - olen := len(dst) - for strconv.IntSize >= 64 && ilen-si >= 8 && olen-n >= 8 { - if ok := enc.decode64(dst[n:], src[si:]); ok { + for strconv.IntSize >= 64 && len(src)-si >= 8 && len(dst)-n >= 8 { + if dn, ok := enc.decode64(src[si:]); ok { + binary.BigEndian.PutUint64(dst[n:], dn) n += 6 si += 8 } else { @@ -481,8 +480,9 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { } } - for ilen-si >= 4 && olen-n >= 4 { - if ok := enc.decode32(dst[n:], src[si:]); ok { + for len(src)-si >= 4 && len(dst)-n >= 4 { + if dn, ok := enc.decode32(src[si:]); ok { + binary.BigEndian.PutUint32(dst[n:], dn) n += 3 si += 4 } else { @@ -506,72 +506,70 @@ func (enc *Encoding) Decode(dst, src []byte) (n int, err error) { return n, err } -// decode32 tries to decode 4 base64 char into 3 bytes. -// len(dst) and len(src) must both be >= 4. -// Returns true if decode succeeded. -func (enc *Encoding) decode32(dst, src []byte) bool { - var dn, n uint32 +// decode32 tries to decode 4 base64 characters into 3 bytes, and returns those +// bytes. len(src) must be >= 4. +// Returns (0, false) if decoding failed. +func (enc *Encoding) decode32(src []byte) (dn uint32, ok bool) { + var n uint32 + _ = src[3] if n = uint32(enc.decodeMap[src[0]]); n == 0xff { - return false + return 0, false } dn |= n << 26 if n = uint32(enc.decodeMap[src[1]]); n == 0xff { - return false + return 0, false } dn |= n << 20 if n = uint32(enc.decodeMap[src[2]]); n == 0xff { - return false + return 0, false } dn |= n << 14 if n = uint32(enc.decodeMap[src[3]]); n == 0xff { - return false + return 0, false } dn |= n << 8 - - binary.BigEndian.PutUint32(dst, dn) - return true + return dn, true } -// decode64 tries to decode 8 base64 char into 6 bytes. -// len(dst) and len(src) must both be >= 8. -// Returns true if decode succeeded. -func (enc *Encoding) decode64(dst, src []byte) bool { - var dn, n uint64 +// decode64 tries to decode 8 base64 characters into 6 bytes, and returns those +// bytes. len(src) must be >= 8. +// Returns (0, false) if decoding failed. +func (enc *Encoding) decode64(src []byte) (dn uint64, ok bool) { + var n uint64 + _ = src[7] if n = uint64(enc.decodeMap[src[0]]); n == 0xff { - return false + return 0, false } dn |= n << 58 if n = uint64(enc.decodeMap[src[1]]); n == 0xff { - return false + return 0, false } dn |= n << 52 if n = uint64(enc.decodeMap[src[2]]); n == 0xff { - return false + return 0, false } dn |= n << 46 if n = uint64(enc.decodeMap[src[3]]); n == 0xff { - return false + return 0, false } dn |= n << 40 if n = uint64(enc.decodeMap[src[4]]); n == 0xff { - return false + return 0, false } dn |= n << 34 if n = uint64(enc.decodeMap[src[5]]); n == 0xff { - return false + return 0, false } dn |= n << 28 if n = uint64(enc.decodeMap[src[6]]); n == 0xff { - return false + return 0, false } dn |= n << 22 if n = uint64(enc.decodeMap[src[7]]); n == 0xff { - return false + return 0, false } dn |= n << 16 - - binary.BigEndian.PutUint64(dst, dn) - return true + return dn, true } type newlineFilteringReader struct { diff --git a/src/encoding/binary/binary.go b/src/encoding/binary/binary.go index 85b3bc2295..8c2d1d9da4 100644 --- a/src/encoding/binary/binary.go +++ b/src/encoding/binary/binary.go @@ -161,23 +161,17 @@ func (bigEndian) GoString() string { return "binary.BigEndian" } func Read(r io.Reader, order ByteOrder, data interface{}) error { // Fast path for basic types and slices. if n := intDataSize(data); n != 0 { - var b [8]byte - var bs []byte - if n > len(b) { - bs = make([]byte, n) - } else { - bs = b[:n] - } + bs := make([]byte, n) if _, err := io.ReadFull(r, bs); err != nil { return err } switch data := data.(type) { case *bool: - *data = b[0] != 0 + *data = bs[0] != 0 case *int8: - *data = int8(b[0]) + *data = int8(bs[0]) case *uint8: - *data = b[0] + *data = bs[0] case *int16: *data = int16(order.Uint16(bs)) case *uint16: @@ -260,25 +254,19 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error { func Write(w io.Writer, order ByteOrder, data interface{}) error { // Fast path for basic types and slices. if n := intDataSize(data); n != 0 { - var b [8]byte - var bs []byte - if n > len(b) { - bs = make([]byte, n) - } else { - bs = b[:n] - } + bs := make([]byte, n) switch v := data.(type) { case *bool: if *v { - b[0] = 1 + bs[0] = 1 } else { - b[0] = 0 + bs[0] = 0 } case bool: if v { - b[0] = 1 + bs[0] = 1 } else { - b[0] = 0 + bs[0] = 0 } case []bool: for i, x := range v { @@ -289,19 +277,19 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error { } } case *int8: - b[0] = byte(*v) + bs[0] = byte(*v) case int8: - b[0] = byte(v) + bs[0] = byte(v) case []int8: for i, x := range v { bs[i] = byte(x) } case *uint8: - b[0] = *v + bs[0] = *v case uint8: - b[0] = v + bs[0] = v case []uint8: - bs = v + bs = v // TODO(josharian): avoid allocating bs in this case? case *int16: order.PutUint16(bs, uint16(*v)) case int16: diff --git a/src/encoding/hex/hex.go b/src/encoding/hex/hex.go index aee5aecb1a..2bb2b57df9 100644 --- a/src/encoding/hex/hex.go +++ b/src/encoding/hex/hex.go @@ -6,10 +6,10 @@ package hex import ( - "bytes" "errors" "fmt" "io" + "strings" ) const hextable = "0123456789abcdef" @@ -116,7 +116,16 @@ func DecodeString(s string) ([]byte, error) { // Dump returns a string that contains a hex dump of the given data. The format // of the hex dump matches the output of `hexdump -C` on the command line. func Dump(data []byte) string { - var buf bytes.Buffer + if len(data) == 0 { + return "" + } + + var buf strings.Builder + // Dumper will write 79 bytes per complete 16 byte chunk, and at least + // 64 bytes for whatever remains. Round the allocation up, since only a + // maximum of 15 bytes will be wasted. + buf.Grow((1 + ((len(data) - 1) / 16)) * 79) + dumper := Dumper(&buf) dumper.Write(data) dumper.Close() diff --git a/src/encoding/hex/hex_test.go b/src/encoding/hex/hex_test.go index 6ba054ef9a..e9f4b3a53a 100644 --- a/src/encoding/hex/hex_test.go +++ b/src/encoding/hex/hex_test.go @@ -248,3 +248,16 @@ func BenchmarkEncode(b *testing.B) { }) } } + +func BenchmarkDump(b *testing.B) { + for _, size := range []int{256, 1024, 4096, 16384} { + src := bytes.Repeat([]byte{2, 3, 5, 7, 9, 11, 13, 17}, size/8) + sink = make([]byte, 2*size) + + b.Run(fmt.Sprintf("%v", size), func(b *testing.B) { + for i := 0; i < b.N; i++ { + Dump(src) + } + }) + } +} diff --git a/src/encoding/json/bench_test.go b/src/encoding/json/bench_test.go index bd322db2e6..72cb349062 100644 --- a/src/encoding/json/bench_test.go +++ b/src/encoding/json/bench_test.go @@ -114,6 +114,34 @@ func BenchmarkCodeMarshal(b *testing.B) { b.SetBytes(int64(len(codeJSON))) } +func benchMarshalBytes(n int) func(*testing.B) { + sample := []byte("hello world") + // Use a struct pointer, to avoid an allocation when passing it as an + // interface parameter to Marshal. + v := &struct { + Bytes []byte + }{ + bytes.Repeat(sample, (n/len(sample))+1)[:n], + } + return func(b *testing.B) { + for i := 0; i < b.N; i++ { + if _, err := Marshal(v); err != nil { + b.Fatal("Marshal:", err) + } + } + } +} + +func BenchmarkMarshalBytes(b *testing.B) { + // 32 fits within encodeState.scratch. + b.Run("32", benchMarshalBytes(32)) + // 256 doesn't fit in encodeState.scratch, but is small enough to + // allocate and avoid the slower base64.NewEncoder. + b.Run("256", benchMarshalBytes(256)) + // 4096 is large enough that we want to avoid allocating for it. + b.Run("4096", benchMarshalBytes(4096)) +} + func BenchmarkCodeDecoder(b *testing.B) { if codeJSON == nil { b.StopTimer() diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 0b29249218..82dc78083a 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -179,7 +179,7 @@ func (d *decodeState) unmarshal(v interface{}) error { // test must be applied at the top level of the value. err := d.value(rv) if err != nil { - return err + return d.addErrorContext(err) } return d.savedError } @@ -267,7 +267,7 @@ type decodeState struct { opcode int // last read result scan scanner errorContext struct { // provides context for type errors - Struct string + Struct reflect.Type Field string } savedError error @@ -289,7 +289,7 @@ func (d *decodeState) init(data []byte) *decodeState { d.data = data d.off = 0 d.savedError = nil - d.errorContext.Struct = "" + d.errorContext.Struct = nil d.errorContext.Field = "" return d } @@ -304,10 +304,10 @@ func (d *decodeState) saveError(err error) { // addErrorContext returns a new error enhanced with information from d.errorContext func (d *decodeState) addErrorContext(err error) error { - if d.errorContext.Struct != "" || d.errorContext.Field != "" { + if d.errorContext.Struct != nil || d.errorContext.Field != "" { switch err := err.(type) { case *UnmarshalTypeError: - err.Struct = d.errorContext.Struct + err.Struct = d.errorContext.Struct.Name() err.Field = d.errorContext.Field return err } @@ -332,13 +332,12 @@ func (d *decodeState) skip() { // scanNext processes the byte at d.data[d.off]. func (d *decodeState) scanNext() { - s, data, i := &d.scan, d.data, d.off - if i < len(data) { - d.opcode = s.step(s, data[i]) - d.off = i + 1 + if d.off < len(d.data) { + d.opcode = d.scan.step(&d.scan, d.data[d.off]) + d.off++ } else { - d.opcode = s.eof() - d.off = len(data) + 1 // mark processed EOF with len+1 + d.opcode = d.scan.eof() + d.off = len(d.data) + 1 // mark processed EOF with len+1 } } @@ -346,7 +345,7 @@ func (d *decodeState) scanNext() { // receives a scan code not equal to op. func (d *decodeState) scanWhile(op int) { s, data, i := &d.scan, d.data, d.off - for i < len(d.data) { + for i < len(data) { newOp := s.step(s, data[i]) i++ if newOp != op { @@ -356,7 +355,7 @@ func (d *decodeState) scanWhile(op int) { } } - d.off = len(d.data) + 1 // mark processed EOF with len+1 + d.off = len(data) + 1 // mark processed EOF with len+1 d.opcode = d.scan.eof() } @@ -413,11 +412,7 @@ func (d *decodeState) valueQuoted() (interface{}, error) { default: return nil, errPhase - case scanBeginArray: - d.skip() - d.scanNext() - - case scanBeginObject: + case scanBeginArray, scanBeginObject: d.skip() d.scanNext() @@ -611,7 +606,7 @@ func (d *decodeState) array(v reflect.Value) error { } var nullLiteral = []byte("null") -var textUnmarshalerType = reflect.TypeOf(new(encoding.TextUnmarshaler)).Elem() +var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() // object consumes an object from d.data[d.off-1:], decoding into v. // The first byte ('{') of the object has been read already. @@ -629,6 +624,7 @@ func (d *decodeState) object(v reflect.Value) error { return nil } v = pv + t := v.Type() // Decoding into nil interface? Switch to non-reflect code. if v.Kind() == reflect.Interface && v.NumMethod() == 0 { @@ -640,6 +636,8 @@ func (d *decodeState) object(v reflect.Value) error { return nil } + var fields []field + // Check type of target: // struct or // map[T1]T2 where T1 is string, an integer type, @@ -648,14 +646,13 @@ func (d *decodeState) object(v reflect.Value) error { case reflect.Map: // Map key must either have string kind, have an integer kind, // or be an encoding.TextUnmarshaler. - t := v.Type() switch t.Key().Kind() { case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: default: if !reflect.PtrTo(t.Key()).Implements(textUnmarshalerType) { - d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)}) d.skip() return nil } @@ -664,14 +661,16 @@ func (d *decodeState) object(v reflect.Value) error { v.Set(reflect.MakeMap(t)) } case reflect.Struct: + fields = cachedTypeFields(t) // ok default: - d.saveError(&UnmarshalTypeError{Value: "object", Type: v.Type(), Offset: int64(d.off)}) + d.saveError(&UnmarshalTypeError{Value: "object", Type: t, Offset: int64(d.off)}) d.skip() return nil } var mapElem reflect.Value + originalErrorContext := d.errorContext for { // Read opening " of string key or closing }. @@ -698,7 +697,7 @@ func (d *decodeState) object(v reflect.Value) error { destring := false // whether the value is wrapped in a string to be decoded first if v.Kind() == reflect.Map { - elemType := v.Type().Elem() + elemType := t.Elem() if !mapElem.IsValid() { mapElem = reflect.New(elemType).Elem() } else { @@ -707,7 +706,6 @@ func (d *decodeState) object(v reflect.Value) error { subv = mapElem } else { var f *field - fields := cachedTypeFields(v.Type()) for i := range fields { ff := &fields[i] if bytes.Equal(ff.nameBytes, key) { @@ -744,7 +742,7 @@ func (d *decodeState) object(v reflect.Value) error { subv = subv.Field(i) } d.errorContext.Field = f.name - d.errorContext.Struct = v.Type().Name() + d.errorContext.Struct = t } else if d.disallowUnknownFields { d.saveError(fmt.Errorf("json: unknown field %q", key)) } @@ -785,13 +783,13 @@ func (d *decodeState) object(v reflect.Value) error { // Write value back to map; // if using struct, subv points into struct already. if v.Kind() == reflect.Map { - kt := v.Type().Key() + kt := t.Key() var kv reflect.Value switch { case kt.Kind() == reflect.String: kv = reflect.ValueOf(key).Convert(kt) case reflect.PtrTo(kt).Implements(textUnmarshalerType): - kv = reflect.New(v.Type().Key()) + kv = reflect.New(kt) if err := d.literalStore(item, kv, true); err != nil { return err } @@ -832,8 +830,7 @@ func (d *decodeState) object(v reflect.Value) error { return errPhase } - d.errorContext.Struct = "" - d.errorContext.Field = "" + d.errorContext = originalErrorContext } return nil } @@ -991,7 +988,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool if fromQuoted { return fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()) } - return &UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())} + d.saveError(&UnmarshalTypeError{Value: "number", Type: v.Type(), Offset: int64(d.readIndex())}) case reflect.Interface: n, err := d.convertNumber(s) if err != nil { diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go index ab83b81bb3..defa97e40f 100644 --- a/src/encoding/json/decode_test.go +++ b/src/encoding/json/decode_test.go @@ -41,6 +41,16 @@ type VOuter struct { V V } +type W struct { + S SS +} + +type SS string + +func (*SS) UnmarshalJSON(data []byte) error { + return &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(SS(""))} +} + // ifaceNumAsFloat64/ifaceNumAsNumber are used to test unmarshaling with and // without UseNumber var ifaceNumAsFloat64 = map[string]interface{}{ @@ -142,7 +152,7 @@ var ( umstructXY = ustructText{unmarshalerText{"x", "y"}} ummapType = map[unmarshalerText]bool{} - ummapXY = map[unmarshalerText]bool{unmarshalerText{"x", "y"}: true} + ummapXY = map[unmarshalerText]bool{{"x", "y"}: true} ) // Test data structures for anonymous fields. @@ -371,6 +381,10 @@ func (b *intWithPtrMarshalText) UnmarshalText(data []byte) error { return (*intWithMarshalText)(b).UnmarshalText(data) } +type mapStringToStringData struct { + Data map[string]string `json:"data"` +} + type unmarshalTest struct { in string ptr interface{} @@ -401,8 +415,10 @@ var unmarshalTests = []unmarshalTest{ {in: `"invalid: \uD834x\uDD1E"`, ptr: new(string), out: "invalid: \uFFFDx\uFFFD"}, {in: "null", ptr: new(interface{}), out: nil}, {in: `{"X": [1,2,3], "Y": 4}`, ptr: new(T), out: T{Y: 4}, err: &UnmarshalTypeError{"array", reflect.TypeOf(""), 7, "T", "X"}}, + {in: `{"X": 23}`, ptr: new(T), out: T{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(""), 8, "T", "X"}}, {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, {in: `{"x": 1}`, ptr: new(tx), out: tx{}}, {in: `{"x": 1}`, ptr: new(tx), err: fmt.Errorf("json: unknown field \"x\""), disallowUnknownFields: true}, + {in: `{"S": 23}`, ptr: new(W), out: W{}, err: &UnmarshalTypeError{"number", reflect.TypeOf(SS("")), 0, "W", "S"}}, {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}}, {in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true}, {in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64}, @@ -866,6 +882,18 @@ var unmarshalTests = []unmarshalTest{ err: fmt.Errorf("json: unknown field \"extra\""), disallowUnknownFields: true, }, + // issue 26444 + // UnmarshalTypeError without field & struct values + { + in: `{"data":{"test1": "bob", "test2": 123}}`, + ptr: new(mapStringToStringData), + err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 37, Struct: "mapStringToStringData", Field: "data"}, + }, + { + in: `{"data":{"test1": 123, "test2": "bob"}}`, + ptr: new(mapStringToStringData), + err: &UnmarshalTypeError{Value: "number", Type: reflect.TypeOf(""), Offset: 21, Struct: "mapStringToStringData", Field: "data"}, + }, } func TestMarshal(t *testing.T) { diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go index 28ca5fe9e0..f10124e67d 100644 --- a/src/encoding/json/encode.go +++ b/src/encoding/json/encode.go @@ -381,8 +381,8 @@ func typeEncoder(t reflect.Type) encoderFunc { } var ( - marshalerType = reflect.TypeOf(new(Marshaler)).Elem() - textMarshalerType = reflect.TypeOf(new(encoding.TextMarshaler)).Elem() + marshalerType = reflect.TypeOf((*Marshaler)(nil)).Elem() + textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() ) // newTypeEncoder constructs an encoderFunc for a type. @@ -624,40 +624,49 @@ func unsupportedTypeEncoder(e *encodeState, v reflect.Value, _ encOpts) { } type structEncoder struct { - fields []field - fieldEncs []encoderFunc + fields []field } -func (se *structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { - e.WriteByte('{') - first := true - for i, f := range se.fields { - fv := fieldByIndex(v, f.index) - if !fv.IsValid() || f.omitEmpty && isEmptyValue(fv) { +func (se structEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { + next := byte('{') +FieldLoop: + for i := range se.fields { + f := &se.fields[i] + + // Find the nested struct field by following f.index. + fv := v + for _, i := range f.index { + if fv.Kind() == reflect.Ptr { + if fv.IsNil() { + continue FieldLoop + } + fv = fv.Elem() + } + fv = fv.Field(i) + } + + if f.omitEmpty && isEmptyValue(fv) { continue } - if first { - first = false + e.WriteByte(next) + next = ',' + if opts.escapeHTML { + e.WriteString(f.nameEscHTML) } else { - e.WriteByte(',') + e.WriteString(f.nameNonEsc) } - e.string(f.name, opts.escapeHTML) - e.WriteByte(':') opts.quoted = f.quoted - se.fieldEncs[i](e, fv, opts) + f.encoder(e, fv, opts) + } + if next == '{' { + e.WriteString("{}") + } else { + e.WriteByte('}') } - e.WriteByte('}') } func newStructEncoder(t reflect.Type) encoderFunc { - fields := cachedTypeFields(t) - se := &structEncoder{ - fields: fields, - fieldEncs: make([]encoderFunc, len(fields)), - } - for i, f := range fields { - se.fieldEncs[i] = typeEncoder(typeByIndex(t, f.index)) - } + se := structEncoder{fields: cachedTypeFields(t)} return se.encode } @@ -665,7 +674,7 @@ type mapEncoder struct { elemEnc encoderFunc } -func (me *mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.IsNil() { e.WriteString("null") return @@ -704,7 +713,7 @@ func newMapEncoder(t reflect.Type) encoderFunc { return unsupportedTypeEncoder } } - me := &mapEncoder{typeEncoder(t.Elem())} + me := mapEncoder{typeEncoder(t.Elem())} return me.encode } @@ -715,14 +724,22 @@ func encodeByteSlice(e *encodeState, v reflect.Value, _ encOpts) { } s := v.Bytes() e.WriteByte('"') - if len(s) < 1024 { - // for small buffers, using Encode directly is much faster. - dst := make([]byte, base64.StdEncoding.EncodedLen(len(s))) + encodedLen := base64.StdEncoding.EncodedLen(len(s)) + if encodedLen <= len(e.scratch) { + // If the encoded bytes fit in e.scratch, avoid an extra + // allocation and use the cheaper Encoding.Encode. + dst := e.scratch[:encodedLen] + base64.StdEncoding.Encode(dst, s) + e.Write(dst) + } else if encodedLen <= 1024 { + // The encoded bytes are short enough to allocate for, and + // Encoding.Encode is still cheaper. + dst := make([]byte, encodedLen) base64.StdEncoding.Encode(dst, s) e.Write(dst) } else { - // for large buffers, avoid unnecessary extra temporary - // buffer space. + // The encoded bytes are too long to cheaply allocate, and + // Encoding.Encode is no longer noticeably cheaper. enc := base64.NewEncoder(base64.StdEncoding, e) enc.Write(s) enc.Close() @@ -735,7 +752,7 @@ type sliceEncoder struct { arrayEnc encoderFunc } -func (se *sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.IsNil() { e.WriteString("null") return @@ -751,7 +768,7 @@ func newSliceEncoder(t reflect.Type) encoderFunc { return encodeByteSlice } } - enc := &sliceEncoder{newArrayEncoder(t)} + enc := sliceEncoder{newArrayEncoder(t)} return enc.encode } @@ -759,7 +776,7 @@ type arrayEncoder struct { elemEnc encoderFunc } -func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (ae arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteByte('[') n := v.Len() for i := 0; i < n; i++ { @@ -772,7 +789,7 @@ func (ae *arrayEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { } func newArrayEncoder(t reflect.Type) encoderFunc { - enc := &arrayEncoder{typeEncoder(t.Elem())} + enc := arrayEncoder{typeEncoder(t.Elem())} return enc.encode } @@ -780,7 +797,7 @@ type ptrEncoder struct { elemEnc encoderFunc } -func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (pe ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.IsNil() { e.WriteString("null") return @@ -789,7 +806,7 @@ func (pe *ptrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { } func newPtrEncoder(t reflect.Type) encoderFunc { - enc := &ptrEncoder{typeEncoder(t.Elem())} + enc := ptrEncoder{typeEncoder(t.Elem())} return enc.encode } @@ -797,7 +814,7 @@ type condAddrEncoder struct { canAddrEnc, elseEnc encoderFunc } -func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { +func (ce condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { if v.CanAddr() { ce.canAddrEnc(e, v, opts) } else { @@ -808,7 +825,7 @@ func (ce *condAddrEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) // newCondAddrEncoder returns an encoder that checks whether its value // CanAddr and delegates to canAddrEnc if so, else to elseEnc. func newCondAddrEncoder(canAddrEnc, elseEnc encoderFunc) encoderFunc { - enc := &condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} + enc := condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc} return enc.encode } @@ -822,28 +839,13 @@ func isValidTag(s string) bool { // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. - default: - if !unicode.IsLetter(c) && !unicode.IsDigit(c) { - return false - } + case !unicode.IsLetter(c) && !unicode.IsDigit(c): + return false } } return true } -func fieldByIndex(v reflect.Value, index []int) reflect.Value { - for _, i := range index { - if v.Kind() == reflect.Ptr { - if v.IsNil() { - return reflect.Value{} - } - v = v.Elem() - } - v = v.Field(i) - } - return v -} - func typeByIndex(t reflect.Type, index []int) reflect.Type { for _, i := range index { if t.Kind() == reflect.Ptr { @@ -893,18 +895,15 @@ func (e *encodeState) string(s string, escapeHTML bool) { if start < i { e.WriteString(s[start:i]) } + e.WriteByte('\\') switch b { case '\\', '"': - e.WriteByte('\\') e.WriteByte(b) case '\n': - e.WriteByte('\\') e.WriteByte('n') case '\r': - e.WriteByte('\\') e.WriteByte('r') case '\t': - e.WriteByte('\\') e.WriteByte('t') default: // This encodes bytes < 0x20 except for \t, \n and \r. @@ -912,7 +911,7 @@ func (e *encodeState) string(s string, escapeHTML bool) { // because they can lead to security holes when // user-controlled strings are rendered into JSON // and served to some browsers. - e.WriteString(`\u00`) + e.WriteString(`u00`) e.WriteByte(hex[b>>4]) e.WriteByte(hex[b&0xF]) } @@ -968,18 +967,15 @@ func (e *encodeState) stringBytes(s []byte, escapeHTML bool) { if start < i { e.Write(s[start:i]) } + e.WriteByte('\\') switch b { case '\\', '"': - e.WriteByte('\\') e.WriteByte(b) case '\n': - e.WriteByte('\\') e.WriteByte('n') case '\r': - e.WriteByte('\\') e.WriteByte('r') case '\t': - e.WriteByte('\\') e.WriteByte('t') default: // This encodes bytes < 0x20 except for \t, \n and \r. @@ -987,7 +983,7 @@ func (e *encodeState) stringBytes(s []byte, escapeHTML bool) { // because they can lead to security holes when // user-controlled strings are rendered into JSON // and served to some browsers. - e.WriteString(`\u00`) + e.WriteString(`u00`) e.WriteByte(hex[b>>4]) e.WriteByte(hex[b&0xF]) } @@ -1036,17 +1032,16 @@ type field struct { nameBytes []byte // []byte(name) equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent + nameNonEsc string // `"` + name + `":` + nameEscHTML string // `"` + HTMLEscape(name) + `":` + tag bool index []int typ reflect.Type omitEmpty bool quoted bool -} -func fillField(f field) field { - f.nameBytes = []byte(f.name) - f.equalFold = foldFunc(f.nameBytes) - return f + encoder encoderFunc } // byIndex sorts field by index sequence. @@ -1086,6 +1081,9 @@ func typeFields(t reflect.Type) []field { // Fields found. var fields []field + // Buffer to run HTMLEscape on field names. + var nameEscBuf bytes.Buffer + for len(next) > 0 { current, next = next, current[:0] count, nextCount = nextCount, map[reflect.Type]int{} @@ -1152,14 +1150,26 @@ func typeFields(t reflect.Type) []field { if name == "" { name = sf.Name } - fields = append(fields, fillField(field{ + field := field{ name: name, tag: tagged, index: index, typ: ft, omitEmpty: opts.Contains("omitempty"), quoted: quoted, - })) + } + field.nameBytes = []byte(field.name) + field.equalFold = foldFunc(field.nameBytes) + + // Build nameEscHTML and nameNonEsc ahead of time. + nameEscBuf.Reset() + nameEscBuf.WriteString(`"`) + HTMLEscape(&nameEscBuf, field.nameBytes) + nameEscBuf.WriteString(`":`) + field.nameEscHTML = nameEscBuf.String() + field.nameNonEsc = `"` + field.name + `":` + + fields = append(fields, field) if count[f.typ] > 1 { // If there were multiple instances, add a second, // so that the annihilation code will see a duplicate. @@ -1173,7 +1183,7 @@ func typeFields(t reflect.Type) []field { // Record new anonymous struct to explore in next round. nextCount[ft]++ if nextCount[ft] == 1 { - next = append(next, fillField(field{name: ft.Name(), index: index, typ: ft})) + next = append(next, field{name: ft.Name(), index: index, typ: ft}) } } } @@ -1227,6 +1237,10 @@ func typeFields(t reflect.Type) []field { fields = out sort.Sort(byIndex(fields)) + for i := range fields { + f := &fields[i] + f.encoder = typeEncoder(typeByIndex(t, f.index)) + } return fields } diff --git a/src/encoding/json/encode_test.go b/src/encoding/json/encode_test.go index b90483cf35..cd5eadf3c1 100644 --- a/src/encoding/json/encode_test.go +++ b/src/encoding/json/encode_test.go @@ -405,6 +405,19 @@ func TestAnonymousFields(t *testing.T) { return S{s1{1, 2, s2{3, 4}}, 6} }, want: `{"MyInt1":1,"MyInt2":3}`, + }, { + // If an anonymous struct pointer field is nil, we should ignore + // the embedded fields behind it. Not properly doing so may + // result in the wrong output or reflect panics. + label: "EmbeddedFieldBehindNilPointer", + makeInput: func() interface{} { + type ( + S2 struct{ Field string } + S struct{ *S2 } + ) + return S{} + }, + want: `{}`, }} for _, tt := range tests { @@ -995,3 +1008,18 @@ func TestMarshalPanic(t *testing.T) { Marshal(&marshalPanic{}) t.Error("Marshal should have panicked") } + +func TestMarshalUncommonFieldNames(t *testing.T) { + v := struct { + A0, À, Aβ int + }{} + b, err := Marshal(v) + if err != nil { + t.Fatal("Marshal:", err) + } + want := `{"A0":0,"À":0,"Aβ":0}` + got := string(b) + if got != want { + t.Fatalf("Marshal: got %s want %s", got, want) + } +} diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go index 75a4270df7..7d5137fbc7 100644 --- a/src/encoding/json/stream.go +++ b/src/encoding/json/stream.go @@ -96,19 +96,19 @@ Input: // Look in the buffer for a new value. for i, c := range dec.buf[scanp:] { dec.scan.bytes++ - v := dec.scan.step(&dec.scan, c) - if v == scanEnd { + switch dec.scan.step(&dec.scan, c) { + case scanEnd: scanp += i break Input - } - // scanEnd is delayed one byte. - // We might block trying to get that byte from src, - // so instead invent a space byte. - if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd { - scanp += i + 1 - break Input - } - if v == scanError { + case scanEndObject, scanEndArray: + // scanEnd is delayed one byte. + // We might block trying to get that byte from src, + // so instead invent a space byte. + if stateEndValue(&dec.scan, ' ') == scanEnd { + scanp += i + 1 + break Input + } + case scanError: dec.err = dec.scan.err return 0, dec.scan.err } @@ -471,7 +471,7 @@ func (dec *Decoder) tokenError(c byte) (Token, error) { case tokenObjectComma: context = " after object key:value pair" } - return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, dec.offset()} + return nil, &SyntaxError{"invalid character " + quoteChar(c) + context, dec.offset()} } // More reports whether there is another element in the diff --git a/src/encoding/json/stream_test.go b/src/encoding/json/stream_test.go index 83c01d170c..aaf32e0a24 100644 --- a/src/encoding/json/stream_test.go +++ b/src/encoding/json/stream_test.go @@ -93,6 +93,10 @@ func TestEncoderIndent(t *testing.T) { func TestEncoderSetEscapeHTML(t *testing.T) { var c C var ct CText + var tagStruct struct { + Valid int `json:"<>&#! "` + Invalid int `json:"\\"` + } for _, tt := range []struct { name string v interface{} @@ -102,6 +106,11 @@ func TestEncoderSetEscapeHTML(t *testing.T) { {"c", c, `"\u003c\u0026\u003e"`, `"<&>"`}, {"ct", ct, `"\"\u003c\u0026\u003e\""`, `"\"<&>\""`}, {`"<&>"`, "<&>", `"\u003c\u0026\u003e"`, `"<&>"`}, + { + "tagStruct", tagStruct, + `{"\u003c\u003e\u0026#! ":0,"Invalid":0}`, + `{"<>&#! ":0,"Invalid":0}`, + }, } { var buf bytes.Buffer enc := NewEncoder(&buf) @@ -192,10 +201,9 @@ func nlines(s string, n int) string { } func TestRawMessage(t *testing.T) { - // TODO(rsc): Should not need the * in *RawMessage var data struct { X float64 - Id *RawMessage + Id RawMessage Y float32 } const raw = `["\u0056",null]` @@ -204,8 +212,8 @@ func TestRawMessage(t *testing.T) { if err != nil { t.Fatalf("Unmarshal: %v", err) } - if string([]byte(*data.Id)) != raw { - t.Fatalf("Raw mismatch: have %#q want %#q", []byte(*data.Id), raw) + if string([]byte(data.Id)) != raw { + t.Fatalf("Raw mismatch: have %#q want %#q", []byte(data.Id), raw) } b, err := Marshal(&data) if err != nil { @@ -217,20 +225,22 @@ func TestRawMessage(t *testing.T) { } func TestNullRawMessage(t *testing.T) { - // TODO(rsc): Should not need the * in *RawMessage var data struct { - X float64 - Id *RawMessage - Y float32 + X float64 + Id RawMessage + IdPtr *RawMessage + Y float32 } - data.Id = new(RawMessage) - const msg = `{"X":0.1,"Id":null,"Y":0.2}` + const msg = `{"X":0.1,"Id":null,"IdPtr":null,"Y":0.2}` err := Unmarshal([]byte(msg), &data) if err != nil { t.Fatalf("Unmarshal: %v", err) } - if data.Id != nil { - t.Fatalf("Raw mismatch: have non-nil, want nil") + if want, got := "null", string(data.Id); want != got { + t.Fatalf("Raw mismatch: have %q, want %q", got, want) + } + if data.IdPtr != nil { + t.Fatalf("Raw pointer mismatch: have non-nil, want nil") } b, err := Marshal(&data) if err != nil { diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go index 6a17516218..a1b5afac08 100644 --- a/src/encoding/pem/pem_test.go +++ b/src/encoding/pem/pem_test.go @@ -213,7 +213,9 @@ func TestFuzz(t *testing.T) { } testRoundtrip := func(block Block) bool { - if isBad(block.Type) { + // Reject bad Type + // Type with colons will proceed as key/val pair and cause an error. + if isBad(block.Type) || strings.Contains(block.Type, ":") { return true } for key, val := range block.Headers { diff --git a/src/encoding/xml/xml.go b/src/encoding/xml/xml.go index 452caefab4..ca059440a1 100644 --- a/src/encoding/xml/xml.go +++ b/src/encoding/xml/xml.go @@ -167,9 +167,9 @@ type Decoder struct { // // Setting: // - // d.Strict = false; - // d.AutoClose = HTMLAutoClose; - // d.Entity = HTMLEntity + // d.Strict = false + // d.AutoClose = xml.HTMLAutoClose + // d.Entity = xml.HTMLEntity // // creates a parser that can handle typical HTML. // @@ -1581,7 +1581,9 @@ var second = &unicode.RangeTable{ // HTMLEntity is an entity map containing translations for the // standard HTML entity characters. -var HTMLEntity = htmlEntity +// +// See the Decoder.Strict and Decoder.Entity fields' documentation. +var HTMLEntity map[string]string = htmlEntity var htmlEntity = map[string]string{ /* @@ -1848,7 +1850,9 @@ var htmlEntity = map[string]string{ // HTMLAutoClose is the set of HTML elements that // should be considered to close automatically. -var HTMLAutoClose = htmlAutoClose +// +// See the Decoder.Strict and Decoder.Entity fields' documentation. +var HTMLAutoClose []string = htmlAutoClose var htmlAutoClose = []string{ /* |
