encoding/asn1: optimize asn1.Unmarshal

Used type-switch instead of switch by reflect.Type and added
BenchmarkUnmarshal.

name         old time/op    new time/op    delta
Marshal-8      28.1µs ± 2%    27.9µs ± 1%     ~     (p=0.094 n=9+9)
Unmarshal-8    6.45µs ± 1%    5.83µs ± 4%   -9.59%  (p=0.000 n=10+10)

name         old alloc/op   new alloc/op   delta
Marshal-8      8.26kB ± 0%    8.26kB ± 0%     ~     (all equal)
Unmarshal-8      840B ± 0%      488B ± 0%  -41.90%  (p=0.000 n=10+10)

name         old allocs/op  new allocs/op  delta
Marshal-8         363 ± 0%       363 ± 0%     ~     (all equal)
Unmarshal-8      50.0 ± 0%      43.0 ± 0%  -14.00%  (p=0.000 n=10+10)

Change-Id: I6b53833c7a3e2524f025453311841d03c1256a45
GitHub-Pull-Request: golang/go#36341
Reviewed-on: https://go-review.googlesource.com/c/go/+/268557
Trust: Filippo Valsorda <filippo@golang.org>
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index 068594e..7c260b4 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -851,53 +851,37 @@
 	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)
+			*v, err = parseUTCTime(innerBytes)
+			return
 		}
-		if err1 == nil {
-			v.Set(reflect.ValueOf(time))
-		}
-		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
diff --git a/src/encoding/asn1/marshal_test.go b/src/encoding/asn1/marshal_test.go
index 5290522..e3a7d8f 100644
--- a/src/encoding/asn1/marshal_test.go
+++ b/src/encoding/asn1/marshal_test.go
@@ -376,3 +376,31 @@
 		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)
+		}
+	}
+}