diff options
Diffstat (limited to 'libgo/go/encoding/asn1')
-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 |
4 files changed, 106 insertions, 40 deletions
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) + } + } +} |