diff options
Diffstat (limited to 'libgo/go/encoding')
-rw-r--r-- | libgo/go/encoding/ascii85/ascii85_test.go | 7 | ||||
-rw-r--r-- | libgo/go/encoding/asn1/asn1.go | 84 | ||||
-rw-r--r-- | libgo/go/encoding/asn1/asn1_test.go | 23 | ||||
-rw-r--r-- | libgo/go/encoding/asn1/common.go | 11 | ||||
-rw-r--r-- | libgo/go/encoding/asn1/marshal_test.go | 28 | ||||
-rw-r--r-- | libgo/go/encoding/base32/base32_test.go | 13 | ||||
-rw-r--r-- | libgo/go/encoding/base64/base64_test.go | 15 | ||||
-rw-r--r-- | libgo/go/encoding/binary/binary.go | 2 | ||||
-rw-r--r-- | libgo/go/encoding/binary/binary_test.go | 27 | ||||
-rw-r--r-- | libgo/go/encoding/binary/varint.go | 4 | ||||
-rw-r--r-- | libgo/go/encoding/gob/encoder_test.go | 4 | ||||
-rw-r--r-- | libgo/go/encoding/hex/hex_test.go | 3 | ||||
-rw-r--r-- | libgo/go/encoding/json/bench_test.go | 8 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode.go | 31 | ||||
-rw-r--r-- | libgo/go/encoding/json/encode_test.go | 27 | ||||
-rw-r--r-- | libgo/go/encoding/json/stream_test.go | 5 | ||||
-rw-r--r-- | libgo/go/encoding/json/tagkey_test.go | 4 | ||||
-rw-r--r-- | libgo/go/encoding/pem/pem_test.go | 4 | ||||
-rw-r--r-- | libgo/go/encoding/xml/marshal.go | 7 | ||||
-rw-r--r-- | libgo/go/encoding/xml/marshal_test.go | 24 |
20 files changed, 248 insertions, 83 deletions
diff --git a/libgo/go/encoding/ascii85/ascii85_test.go b/libgo/go/encoding/ascii85/ascii85_test.go index 1a3a87a..c6371039 100644 --- a/libgo/go/encoding/ascii85/ascii85_test.go +++ b/libgo/go/encoding/ascii85/ascii85_test.go @@ -7,7 +7,6 @@ package ascii85 import ( "bytes" "io" - "io/ioutil" "strings" "testing" ) @@ -118,7 +117,7 @@ func TestDecode(t *testing.T) { func TestDecoder(t *testing.T) { for _, p := range pairs { decoder := NewDecoder(strings.NewReader(p.encoded)) - dbuf, err := ioutil.ReadAll(decoder) + dbuf, err := io.ReadAll(decoder) if err != nil { t.Fatal("Read failed", err) } @@ -187,7 +186,7 @@ func TestBig(t *testing.T) { if err != nil { t.Fatalf("Encoder.Close() = %v want nil", err) } - decoded, err := ioutil.ReadAll(NewDecoder(encoded)) + decoded, err := io.ReadAll(NewDecoder(encoded)) if err != nil { t.Fatalf("io.ReadAll(NewDecoder(...)): %v", err) } @@ -205,7 +204,7 @@ func TestBig(t *testing.T) { func TestDecoderInternalWhitespace(t *testing.T) { s := strings.Repeat(" ", 2048) + "z" - decoded, err := ioutil.ReadAll(NewDecoder(strings.NewReader(s))) + decoded, err := io.ReadAll(NewDecoder(strings.NewReader(s))) if err != nil { t.Errorf("Decode gave error %v", err) } diff --git a/libgo/go/encoding/asn1/asn1.go b/libgo/go/encoding/asn1/asn1.go index d809dde..7c260b4 100644 --- a/libgo/go/encoding/asn1/asn1.go +++ b/libgo/go/encoding/asn1/asn1.go @@ -851,53 +851,37 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam offset += t.length // We deal with the structures defined in this package first. - switch fieldType { - case rawValueType: - result := RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]} - v.Set(reflect.ValueOf(result)) + switch v := v.Addr().Interface().(type) { + case *RawValue: + *v = RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]} return - case objectIdentifierType: - newSlice, err1 := parseObjectIdentifier(innerBytes) - v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice))) - if err1 == nil { - reflect.Copy(v, reflect.ValueOf(newSlice)) - } - err = err1 + case *ObjectIdentifier: + *v, err = parseObjectIdentifier(innerBytes) return - case bitStringType: - bs, err1 := parseBitString(innerBytes) - if err1 == nil { - v.Set(reflect.ValueOf(bs)) - } - err = err1 + case *BitString: + *v, err = parseBitString(innerBytes) return - case timeType: - var time time.Time - var err1 error + case *time.Time: if universalTag == TagUTCTime { - time, err1 = parseUTCTime(innerBytes) - } else { - time, err1 = parseGeneralizedTime(innerBytes) - } - if err1 == nil { - v.Set(reflect.ValueOf(time)) + *v, err = parseUTCTime(innerBytes) + return } - err = err1 + *v, err = parseGeneralizedTime(innerBytes) return - case enumeratedType: + case *Enumerated: parsedInt, err1 := parseInt32(innerBytes) if err1 == nil { - v.SetInt(int64(parsedInt)) + *v = Enumerated(parsedInt) } err = err1 return - case flagType: - v.SetBool(true) + case *Flag: + *v = true return - case bigIntType: + case **big.Int: parsedInt, err1 := parseBigInt(innerBytes) if err1 == nil { - v.Set(reflect.ValueOf(parsedInt)) + *v = parsedInt } err = err1 return @@ -1035,7 +1019,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // Unmarshal parses the DER-encoded ASN.1 data structure b // and uses the reflect package to fill in an arbitrary value pointed at by val. // Because Unmarshal uses the reflect package, the structs -// being written to must use upper case field names. +// being written to must use upper case field names. If val +// is nil or not a pointer, Unmarshal returns an error. // // After parsing b, any bytes that were leftover and not used to fill // val will be returned in rest. When parsing a SEQUENCE into a struct, @@ -1085,9 +1070,10 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) { // If the type of the first field of a structure is RawContent then the raw // ASN1 contents of the struct will be stored in it. // -// If the type name of a slice element ends with "SET" then it's treated as if -// the "set" tag was set on it. This can be used with nested slices where a -// struct tag cannot be given. +// If the name of a slice type ends with "SET" then it's treated as if +// the "set" tag was set on it. This results in interpreting the type as a +// SET OF x rather than a SEQUENCE OF x. This can be used with nested slices +// where a struct tag cannot be given. // // Other ASN.1 types are not supported; if it encounters them, // Unmarshal returns a parse error. @@ -1095,11 +1081,31 @@ func Unmarshal(b []byte, val interface{}) (rest []byte, err error) { return UnmarshalWithParams(b, val, "") } +// An invalidUnmarshalError describes an invalid argument passed to Unmarshal. +// (The argument to Unmarshal must be a non-nil pointer.) +type invalidUnmarshalError struct { + Type reflect.Type +} + +func (e *invalidUnmarshalError) Error() string { + if e.Type == nil { + return "asn1: Unmarshal recipient value is nil" + } + + if e.Type.Kind() != reflect.Ptr { + return "asn1: Unmarshal recipient value is non-pointer " + e.Type.String() + } + return "asn1: Unmarshal recipient value is nil " + e.Type.String() +} + // UnmarshalWithParams allows field parameters to be specified for the // top-level element. The form of the params is the same as the field tags. func UnmarshalWithParams(b []byte, val interface{}, params string) (rest []byte, err error) { - v := reflect.ValueOf(val).Elem() - offset, err := parseField(v, b, 0, parseFieldParameters(params)) + v := reflect.ValueOf(val) + if v.Kind() != reflect.Ptr || v.IsNil() { + return nil, &invalidUnmarshalError{reflect.TypeOf(val)} + } + offset, err := parseField(v.Elem(), b, 0, parseFieldParameters(params)) if err != nil { return nil, err } diff --git a/libgo/go/encoding/asn1/asn1_test.go b/libgo/go/encoding/asn1/asn1_test.go index 8daae97..8985538 100644 --- a/libgo/go/encoding/asn1/asn1_test.go +++ b/libgo/go/encoding/asn1/asn1_test.go @@ -518,6 +518,29 @@ func TestUnmarshal(t *testing.T) { } } +func TestUnmarshalWithNilOrNonPointer(t *testing.T) { + tests := []struct { + b []byte + v interface{} + want string + }{ + {b: []byte{0x05, 0x00}, v: nil, want: "asn1: Unmarshal recipient value is nil"}, + {b: []byte{0x05, 0x00}, v: RawValue{}, want: "asn1: Unmarshal recipient value is non-pointer asn1.RawValue"}, + {b: []byte{0x05, 0x00}, v: (*RawValue)(nil), want: "asn1: Unmarshal recipient value is nil *asn1.RawValue"}, + } + + for _, test := range tests { + _, err := Unmarshal(test.b, test.v) + if err == nil { + t.Errorf("Unmarshal expecting error, got nil") + continue + } + if g, w := err.Error(), test.want; g != w { + t.Errorf("InvalidUnmarshalError mismatch\nGot: %q\nWant: %q", g, w) + } + } +} + type Certificate struct { TBSCertificate TBSCertificate SignatureAlgorithm AlgorithmIdentifier diff --git a/libgo/go/encoding/asn1/common.go b/libgo/go/encoding/asn1/common.go index e2aa8bd..1c712e1 100644 --- a/libgo/go/encoding/asn1/common.go +++ b/libgo/go/encoding/asn1/common.go @@ -92,7 +92,16 @@ type fieldParameters struct { // parseFieldParameters will parse it into a fieldParameters structure, // ignoring unknown parts of the string. func parseFieldParameters(str string) (ret fieldParameters) { - for _, part := range strings.Split(str, ",") { + var part string + for len(str) > 0 { + // This loop uses IndexByte and explicit slicing + // instead of strings.Split(str, ",") to reduce allocations. + i := strings.IndexByte(str, ',') + if i < 0 { + part, str = str, "" + } else { + part, str = str[:i], str[i+1:] + } switch { case part == "optional": ret.optional = true diff --git a/libgo/go/encoding/asn1/marshal_test.go b/libgo/go/encoding/asn1/marshal_test.go index 5290522..e3a7d8f 100644 --- a/libgo/go/encoding/asn1/marshal_test.go +++ b/libgo/go/encoding/asn1/marshal_test.go @@ -376,3 +376,31 @@ func TestSetEncoderSETSliceSuffix(t *testing.T) { t.Errorf("Unexpected SET content. got: %s, want: %s", resultSet, expectedOrder) } } + +func BenchmarkUnmarshal(b *testing.B) { + b.ReportAllocs() + + type testCase struct { + in []byte + out interface{} + } + var testData []testCase + for _, test := range unmarshalTestData { + pv := reflect.New(reflect.TypeOf(test.out).Elem()) + inCopy := make([]byte, len(test.in)) + copy(inCopy, test.in) + outCopy := pv.Interface() + + testData = append(testData, testCase{ + in: inCopy, + out: outCopy, + }) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + for _, testCase := range testData { + _, _ = Unmarshal(testCase.in, testCase.out) + } + } +} diff --git a/libgo/go/encoding/base32/base32_test.go b/libgo/go/encoding/base32/base32_test.go index 0b611db..8fb22b9 100644 --- a/libgo/go/encoding/base32/base32_test.go +++ b/libgo/go/encoding/base32/base32_test.go @@ -8,7 +8,6 @@ import ( "bytes" "errors" "io" - "io/ioutil" "strings" "testing" ) @@ -361,9 +360,9 @@ func TestBig(t *testing.T) { if err != nil { t.Fatalf("Encoder.Close() = %v want nil", err) } - decoded, err := ioutil.ReadAll(NewDecoder(StdEncoding, encoded)) + decoded, err := io.ReadAll(NewDecoder(StdEncoding, encoded)) if err != nil { - t.Fatalf("ioutil.ReadAll(NewDecoder(...)): %v", err) + t.Fatalf("io.ReadAll(NewDecoder(...)): %v", err) } if !bytes.Equal(raw, decoded) { @@ -428,14 +427,14 @@ LNEBUWIIDFON2CA3DBMJXXE5LNFY== encodedShort := strings.ReplaceAll(encoded, "\n", "") dec := NewDecoder(StdEncoding, strings.NewReader(encoded)) - res1, err := ioutil.ReadAll(dec) + res1, err := io.ReadAll(dec) if err != nil { t.Errorf("ReadAll failed: %v", err) } dec = NewDecoder(StdEncoding, strings.NewReader(encodedShort)) var res2 []byte - res2, err = ioutil.ReadAll(dec) + res2, err = io.ReadAll(dec) if err != nil { t.Errorf("ReadAll failed: %v", err) } @@ -619,7 +618,7 @@ func TestBufferedDecodingSameError(t *testing.T) { }() decoder := NewDecoder(StdEncoding, pr) - _, err := ioutil.ReadAll(decoder) + _, err := io.ReadAll(decoder) if err != testcase.expected { t.Errorf("Expected %v, got %v; case %s %+v", testcase.expected, err, testcase.prefix, chunks) @@ -718,7 +717,7 @@ func TestDecodeReadAll(t *testing.T) { encoded = strings.ReplaceAll(encoded, "=", "") } - decReader, err := ioutil.ReadAll(NewDecoder(encoding, strings.NewReader(encoded))) + decReader, err := io.ReadAll(NewDecoder(encoding, strings.NewReader(encoded))) if err != nil { t.Errorf("NewDecoder error: %v", err) } diff --git a/libgo/go/encoding/base64/base64_test.go b/libgo/go/encoding/base64/base64_test.go index c2c9478..5104740 100644 --- a/libgo/go/encoding/base64/base64_test.go +++ b/libgo/go/encoding/base64/base64_test.go @@ -9,7 +9,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "reflect" "runtime/debug" "strings" @@ -324,9 +323,9 @@ func TestBig(t *testing.T) { if err != nil { t.Fatalf("Encoder.Close() = %v want nil", err) } - decoded, err := ioutil.ReadAll(NewDecoder(StdEncoding, encoded)) + decoded, err := io.ReadAll(NewDecoder(StdEncoding, encoded)) if err != nil { - t.Fatalf("ioutil.ReadAll(NewDecoder(...)): %v", err) + t.Fatalf("io.ReadAll(NewDecoder(...)): %v", err) } if !bytes.Equal(raw, decoded) { @@ -403,7 +402,7 @@ func TestDecoderIssue3577(t *testing.T) { }) errc := make(chan error, 1) go func() { - _, err := ioutil.ReadAll(d) + _, err := io.ReadAll(d) errc <- err }() select { @@ -436,14 +435,14 @@ bqbPb06551Y4 encodedShort := strings.ReplaceAll(encoded, "\n", "") dec := NewDecoder(StdEncoding, strings.NewReader(encoded)) - res1, err := ioutil.ReadAll(dec) + res1, err := io.ReadAll(dec) if err != nil { t.Errorf("ReadAll failed: %v", err) } dec = NewDecoder(StdEncoding, strings.NewReader(encodedShort)) var res2 []byte - res2, err = ioutil.ReadAll(dec) + res2, err = io.ReadAll(dec) if err != nil { t.Errorf("ReadAll failed: %v", err) } @@ -517,14 +516,14 @@ func TestDecoderRaw(t *testing.T) { // Through reader. Used to fail. r := NewDecoder(RawURLEncoding, bytes.NewReader([]byte(source))) - dec2, err := ioutil.ReadAll(io.LimitReader(r, 100)) + dec2, err := io.ReadAll(io.LimitReader(r, 100)) if err != nil || !bytes.Equal(dec2, want) { t.Errorf("reading NewDecoder(RawURLEncoding, %q) = %x, %v, want %x, nil", source, dec2, err, want) } // Should work with padding. r = NewDecoder(URLEncoding, bytes.NewReader([]byte(source+"=="))) - dec3, err := ioutil.ReadAll(r) + dec3, err := io.ReadAll(r) if err != nil || !bytes.Equal(dec3, want) { t.Errorf("reading NewDecoder(URLEncoding, %q) = %x, %v, want %x, nil", source+"==", dec3, err, want) } diff --git a/libgo/go/encoding/binary/binary.go b/libgo/go/encoding/binary/binary.go index a99ca01..a311499 100644 --- a/libgo/go/encoding/binary/binary.go +++ b/libgo/go/encoding/binary/binary.go @@ -306,7 +306,7 @@ func Write(w io.Writer, order ByteOrder, data interface{}) error { case uint8: bs[0] = v case []uint8: - bs = v // TODO(josharian): avoid allocating bs in this case? + bs = v case *int16: order.PutUint16(bs, uint16(*v)) case int16: diff --git a/libgo/go/encoding/binary/binary_test.go b/libgo/go/encoding/binary/binary_test.go index aeb4212..83af89e 100644 --- a/libgo/go/encoding/binary/binary_test.go +++ b/libgo/go/encoding/binary/binary_test.go @@ -8,7 +8,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "math" "reflect" "strings" @@ -524,7 +523,7 @@ func BenchmarkWriteStruct(b *testing.B) { b.SetBytes(int64(Size(&s))) b.ResetTimer() for i := 0; i < b.N; i++ { - Write(ioutil.Discard, BigEndian, &s) + Write(io.Discard, BigEndian, &s) } } @@ -706,3 +705,27 @@ func BenchmarkWriteSlice1000Float32s(b *testing.B) { } b.StopTimer() } + +func BenchmarkReadSlice1000Uint8s(b *testing.B) { + bsr := &byteSliceReader{} + slice := make([]uint8, 1000) + buf := make([]byte, len(slice)) + b.SetBytes(int64(len(buf))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + bsr.remain = buf + Read(bsr, BigEndian, slice) + } +} + +func BenchmarkWriteSlice1000Uint8s(b *testing.B) { + slice := make([]uint8, 1000) + buf := new(bytes.Buffer) + var w io.Writer = buf + b.SetBytes(1000) + b.ResetTimer() + for i := 0; i < b.N; i++ { + buf.Reset() + Write(w, BigEndian, slice) + } +} diff --git a/libgo/go/encoding/binary/varint.go b/libgo/go/encoding/binary/varint.go index 38af610..1fa325d 100644 --- a/libgo/go/encoding/binary/varint.go +++ b/libgo/go/encoding/binary/varint.go @@ -62,7 +62,7 @@ func Uvarint(buf []byte) (uint64, int) { var s uint for i, b := range buf { if b < 0x80 { - if i > 9 || i == 9 && b > 1 { + if i >= MaxVarintLen64 || i == MaxVarintLen64-1 && b > 1 { return 0, -(i + 1) // overflow } return x | uint64(b)<<s, i + 1 @@ -112,7 +112,7 @@ func ReadUvarint(r io.ByteReader) (uint64, error) { return x, err } if b < 0x80 { - if i == 9 && b > 1 { + if i == MaxVarintLen64-1 && b > 1 { return x, overflow } return x | uint64(b)<<s, nil diff --git a/libgo/go/encoding/gob/encoder_test.go b/libgo/go/encoding/gob/encoder_test.go index 825f0d6..fe27749 100644 --- a/libgo/go/encoding/gob/encoder_test.go +++ b/libgo/go/encoding/gob/encoder_test.go @@ -8,7 +8,7 @@ import ( "bytes" "encoding/hex" "fmt" - "io/ioutil" + "io" "reflect" "strings" "testing" @@ -938,7 +938,7 @@ func encodeAndRecover(value interface{}) (encodeErr, panicErr error) { } }() - encodeErr = NewEncoder(ioutil.Discard).Encode(value) + encodeErr = NewEncoder(io.Discard).Encode(value) return } diff --git a/libgo/go/encoding/hex/hex_test.go b/libgo/go/encoding/hex/hex_test.go index 31e3f68..7593e20 100644 --- a/libgo/go/encoding/hex/hex_test.go +++ b/libgo/go/encoding/hex/hex_test.go @@ -8,7 +8,6 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "strings" "testing" ) @@ -150,7 +149,7 @@ func TestEncoderDecoder(t *testing.T) { func TestDecoderErr(t *testing.T) { for _, tt := range errTests { dec := NewDecoder(strings.NewReader(tt.in)) - out, err := ioutil.ReadAll(dec) + out, err := io.ReadAll(dec) wantErr := tt.err // Decoder is reading from stream, so it reports io.ErrUnexpectedEOF instead of ErrLength. if wantErr == ErrLength { diff --git a/libgo/go/encoding/json/bench_test.go b/libgo/go/encoding/json/bench_test.go index 4a5fe7e..73c7b09 100644 --- a/libgo/go/encoding/json/bench_test.go +++ b/libgo/go/encoding/json/bench_test.go @@ -15,7 +15,7 @@ import ( "compress/gzip" "fmt" "internal/testenv" - "io/ioutil" + "io" "os" "reflect" "runtime" @@ -52,7 +52,7 @@ func codeInit() { if err != nil { panic(err) } - data, err := ioutil.ReadAll(gz) + data, err := io.ReadAll(gz) if err != nil { panic(err) } @@ -89,7 +89,7 @@ func BenchmarkCodeEncoder(b *testing.B) { b.StartTimer() } b.RunParallel(func(pb *testing.PB) { - enc := NewEncoder(ioutil.Discard) + enc := NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&codeStruct); err != nil { b.Fatal("Encode:", err) @@ -399,7 +399,7 @@ func BenchmarkEncodeMarshaler(b *testing.B) { }{} b.RunParallel(func(pb *testing.PB) { - enc := NewEncoder(ioutil.Discard) + enc := NewEncoder(io.Discard) for pb.Next() { if err := enc.Encode(&m); err != nil { diff --git a/libgo/go/encoding/json/encode.go b/libgo/go/encoding/json/encode.go index 578d551..483b9d8 100644 --- a/libgo/go/encoding/json/encode.go +++ b/libgo/go/encoding/json/encode.go @@ -236,6 +236,8 @@ func (e *UnsupportedTypeError) Error() string { return "json: unsupported type: " + e.Type.String() } +// An UnsupportedValueError is returned by Marshal when attempting +// to encode an unsupported value. type UnsupportedValueError struct { Value reflect.Value Str string @@ -779,6 +781,16 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } + if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter { + // We're a large number of nested ptrEncoder.encode calls deep; + // start checking if we've run into a pointer cycle. + ptr := v.Pointer() + if _, ok := e.ptrSeen[ptr]; ok { + e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())}) + } + e.ptrSeen[ptr] = struct{}{} + defer delete(e.ptrSeen, ptr) + } e.WriteByte('{') // Extract and sort the keys. @@ -801,6 +813,7 @@ func (me mapEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { me.elemEnc(e, v.MapIndex(kv.v), opts) } e.WriteByte('}') + e.ptrLevel-- } func newMapEncoder(t reflect.Type) encoderFunc { @@ -857,7 +870,23 @@ func (se sliceEncoder) encode(e *encodeState, v reflect.Value, opts encOpts) { e.WriteString("null") return } + if e.ptrLevel++; e.ptrLevel > startDetectingCyclesAfter { + // We're a large number of nested ptrEncoder.encode calls deep; + // start checking if we've run into a pointer cycle. + // Here we use a struct to memorize the pointer to the first element of the slice + // and its length. + ptr := struct { + ptr uintptr + len int + }{v.Pointer(), v.Len()} + if _, ok := e.ptrSeen[ptr]; ok { + e.error(&UnsupportedValueError{v, fmt.Sprintf("encountered a cycle via %s", v.Type())}) + } + e.ptrSeen[ptr] = struct{}{} + defer delete(e.ptrSeen, ptr) + } se.arrayEnc(e, v, opts) + e.ptrLevel-- } func newSliceEncoder(t reflect.Type) encoderFunc { @@ -946,7 +975,7 @@ func isValidTag(s string) bool { } for _, c := range s { switch { - case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c): + case strings.ContainsRune("!#$%&()*+-./:;<=>?@[]^_{|}~ ", c): // Backslash and quote chars are reserved, but // otherwise any punctuation chars are allowed // in a tag name. diff --git a/libgo/go/encoding/json/encode_test.go b/libgo/go/encoding/json/encode_test.go index 7290eca..42bb09d 100644 --- a/libgo/go/encoding/json/encode_test.go +++ b/libgo/go/encoding/json/encode_test.go @@ -183,7 +183,15 @@ type PointerCycleIndirect struct { Ptrs []interface{} } -var pointerCycleIndirect = &PointerCycleIndirect{} +type RecursiveSlice []RecursiveSlice + +var ( + pointerCycleIndirect = &PointerCycleIndirect{} + mapCycle = make(map[string]interface{}) + sliceCycle = []interface{}{nil} + sliceNoCycle = []interface{}{nil, nil} + recursiveSliceCycle = []RecursiveSlice{nil} +) func init() { ptr := &SamePointerNoCycle{} @@ -192,6 +200,14 @@ func init() { pointerCycle.Ptr = pointerCycle pointerCycleIndirect.Ptrs = []interface{}{pointerCycleIndirect} + + mapCycle["x"] = mapCycle + sliceCycle[0] = sliceCycle + sliceNoCycle[1] = sliceNoCycle[:1] + for i := startDetectingCyclesAfter; i > 0; i-- { + sliceNoCycle = []interface{}{sliceNoCycle} + } + recursiveSliceCycle[0] = recursiveSliceCycle } func TestSamePointerNoCycle(t *testing.T) { @@ -200,12 +216,21 @@ func TestSamePointerNoCycle(t *testing.T) { } } +func TestSliceNoCycle(t *testing.T) { + if _, err := Marshal(sliceNoCycle); err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + var unsupportedValues = []interface{}{ math.NaN(), math.Inf(-1), math.Inf(1), pointerCycle, pointerCycleIndirect, + mapCycle, + sliceCycle, + recursiveSliceCycle, } func TestUnsupportedValues(t *testing.T) { diff --git a/libgo/go/encoding/json/stream_test.go b/libgo/go/encoding/json/stream_test.go index c9e5334..c284f2d 100644 --- a/libgo/go/encoding/json/stream_test.go +++ b/libgo/go/encoding/json/stream_test.go @@ -7,7 +7,6 @@ package json import ( "bytes" "io" - "io/ioutil" "log" "net" "net/http" @@ -215,7 +214,7 @@ func TestDecoderBuffered(t *testing.T) { if m.Name != "Gopher" { t.Errorf("Name = %q; want Gopher", m.Name) } - rest, err := ioutil.ReadAll(d.Buffered()) + rest, err := io.ReadAll(d.Buffered()) if err != nil { t.Fatal(err) } @@ -318,7 +317,7 @@ func BenchmarkEncoderEncode(b *testing.B) { v := &T{"foo", "bar"} b.RunParallel(func(pb *testing.PB) { for pb.Next() { - if err := NewEncoder(ioutil.Discard).Encode(v); err != nil { + if err := NewEncoder(io.Discard).Encode(v); err != nil { b.Fatal(err) } } diff --git a/libgo/go/encoding/json/tagkey_test.go b/libgo/go/encoding/json/tagkey_test.go index f77c49c..bbb4e6a 100644 --- a/libgo/go/encoding/json/tagkey_test.go +++ b/libgo/go/encoding/json/tagkey_test.go @@ -41,7 +41,7 @@ type percentSlashTag struct { } type punctuationTag struct { - V string `json:"!#$%&()*+-./:<=>?@[]^_{|}~"` // https://golang.org/issue/3546 + V string `json:"!#$%&()*+-./:;<=>?@[]^_{|}~ "` // https://golang.org/issue/3546 } type dashTag struct { @@ -90,7 +90,7 @@ var structTagObjectKeyTests = []struct { {badFormatTag{"Orfevre"}, "Orfevre", "Y"}, {badCodeTag{"Reliable Man"}, "Reliable Man", "Z"}, {percentSlashTag{"brut"}, "brut", "text/html%"}, - {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:<=>?@[]^_{|}~"}, + {punctuationTag{"Union Rags"}, "Union Rags", "!#$%&()*+-./:;<=>?@[]^_{|}~ "}, {spaceTag{"Perreddu"}, "Perreddu", "With space"}, {unicodeTag{"Loukanikos"}, "Loukanikos", "Ελλάδα"}, } diff --git a/libgo/go/encoding/pem/pem_test.go b/libgo/go/encoding/pem/pem_test.go index 8515b46..b2b6b15 100644 --- a/libgo/go/encoding/pem/pem_test.go +++ b/libgo/go/encoding/pem/pem_test.go @@ -6,7 +6,7 @@ package pem import ( "bytes" - "io/ioutil" + "io" "reflect" "strings" "testing" @@ -271,7 +271,7 @@ func BenchmarkEncode(b *testing.B) { data := &Block{Bytes: make([]byte, 65536)} b.SetBytes(int64(len(data.Bytes))) for i := 0; i < b.N; i++ { - Encode(ioutil.Discard, data) + Encode(io.Discard, data) } } diff --git a/libgo/go/encoding/xml/marshal.go b/libgo/go/encoding/xml/marshal.go index 0554b0d..d8a04a9 100644 --- a/libgo/go/encoding/xml/marshal.go +++ b/libgo/go/encoding/xml/marshal.go @@ -345,8 +345,11 @@ func (p *printer) createAttrPrefix(url string) string { if prefix == "" || !isName([]byte(prefix)) || strings.Contains(prefix, ":") { prefix = "_" } - if strings.HasPrefix(prefix, "xml") { - // xmlanything is reserved. + // xmlanything is reserved and any variant of it regardless of + // case should be matched, so: + // (('X'|'x') ('M'|'m') ('L'|'l')) + // See Section 2.3 of https://www.w3.org/TR/REC-xml/ + if len(prefix) >= 3 && strings.EqualFold(prefix[:3], "xml") { prefix = "_" + prefix } if p.attrNS[prefix] != "" { diff --git a/libgo/go/encoding/xml/marshal_test.go b/libgo/go/encoding/xml/marshal_test.go index 31309ef..d2e5137 100644 --- a/libgo/go/encoding/xml/marshal_test.go +++ b/libgo/go/encoding/xml/marshal_test.go @@ -2283,6 +2283,30 @@ var encodeTokenTests = []struct { }}, }, want: `<foo xmlns="space"><bar xmlns="space" xmlns:space="space" space:attr="value">`, +}, { + desc: "reserved namespace prefix -- all lower case", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"http://www.w3.org/2001/xmlSchema-instance", "nil"}, "true"}, + }}, + }, + want: `<foo xmlns:_xmlSchema-instance="http://www.w3.org/2001/xmlSchema-instance" _xmlSchema-instance:nil="true">`, +}, { + desc: "reserved namespace prefix -- all upper case", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"http://www.w3.org/2001/XMLSchema-instance", "nil"}, "true"}, + }}, + }, + want: `<foo xmlns:_XMLSchema-instance="http://www.w3.org/2001/XMLSchema-instance" _XMLSchema-instance:nil="true">`, +}, { + desc: "reserved namespace prefix -- all mixed case", + toks: []Token{ + StartElement{Name{"", "foo"}, []Attr{ + {Name{"http://www.w3.org/2001/XmLSchema-instance", "nil"}, "true"}, + }}, + }, + want: `<foo xmlns:_XmLSchema-instance="http://www.w3.org/2001/XmLSchema-instance" _XmLSchema-instance:nil="true">`, }} func TestEncodeToken(t *testing.T) { |