aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/encoding
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/encoding')
-rw-r--r--libgo/go/encoding/ascii85/ascii85_test.go7
-rw-r--r--libgo/go/encoding/asn1/asn1.go84
-rw-r--r--libgo/go/encoding/asn1/asn1_test.go23
-rw-r--r--libgo/go/encoding/asn1/common.go11
-rw-r--r--libgo/go/encoding/asn1/marshal_test.go28
-rw-r--r--libgo/go/encoding/base32/base32_test.go13
-rw-r--r--libgo/go/encoding/base64/base64_test.go15
-rw-r--r--libgo/go/encoding/binary/binary.go2
-rw-r--r--libgo/go/encoding/binary/binary_test.go27
-rw-r--r--libgo/go/encoding/binary/varint.go4
-rw-r--r--libgo/go/encoding/gob/encoder_test.go4
-rw-r--r--libgo/go/encoding/hex/hex_test.go3
-rw-r--r--libgo/go/encoding/json/bench_test.go8
-rw-r--r--libgo/go/encoding/json/encode.go31
-rw-r--r--libgo/go/encoding/json/encode_test.go27
-rw-r--r--libgo/go/encoding/json/stream_test.go5
-rw-r--r--libgo/go/encoding/json/tagkey_test.go4
-rw-r--r--libgo/go/encoding/pem/pem_test.go4
-rw-r--r--libgo/go/encoding/xml/marshal.go7
-rw-r--r--libgo/go/encoding/xml/marshal_test.go24
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) {