internal/encoding/json: improve allocation of Value for JSON strings

Substitute interface Value.value field with per type field boo and str
instead.

name      old time/op    new time/op    delta
String-4     286ns ± 0%     254ns ± 0%   ~     (p=1.000 n=1+1)
Bool-4       209ns ± 0%     211ns ± 0%   ~     (p=1.000 n=1+1)

name      old alloc/op   new alloc/op   delta
String-4      192B ± 0%      176B ± 0%   ~     (p=1.000 n=1+1)
Bool-4       0.00B          0.00B        ~     (all equal)

name      old allocs/op  new allocs/op  delta
String-4      4.00 ± 0%      3.00 ± 0%   ~     (p=1.000 n=1+1)
Bool-4        0.00           0.00        ~     (all equal)

Change-Id: Ib0167d22e60d63c221c303b79c75b9e96d432fe7
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170277
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/encoding/json/bench_test.go b/internal/encoding/json/bench_test.go
index 4db8af9..6c3f1d1 100644
--- a/internal/encoding/json/bench_test.go
+++ b/internal/encoding/json/bench_test.go
@@ -11,7 +11,7 @@
 )
 
 func BenchmarkFloat(b *testing.B) {
-	input := []byte("1.797693134862315708145274237317043567981e+308")
+	input := []byte(`1.797693134862315708145274237317043567981e+308`)
 	for i := 0; i < b.N; i++ {
 		dec := json.NewDecoder(input)
 		val, err := dec.Read()
@@ -26,7 +26,7 @@
 }
 
 func BenchmarkInt(b *testing.B) {
-	input := []byte("922337203.6854775807e+10")
+	input := []byte(`922337203.6854775807e+10`)
 	for i := 0; i < b.N; i++ {
 		dec := json.NewDecoder(input)
 		val, err := dec.Read()
@@ -39,3 +39,30 @@
 		}
 	}
 }
+
+func BenchmarkString(b *testing.B) {
+	input := []byte(`"abcdefghijklmnopqrstuvwxyz0123456789\\n\\t"`)
+	for i := 0; i < b.N; i++ {
+		dec := json.NewDecoder(input)
+		val, err := dec.Read()
+		if err != nil {
+			b.Fatal(err)
+		}
+		_ = val.String()
+	}
+}
+
+func BenchmarkBool(b *testing.B) {
+	input := []byte(`true`)
+	for i := 0; i < b.N; i++ {
+		dec := json.NewDecoder(input)
+		val, err := dec.Read()
+		if err != nil {
+			b.Fatal(err)
+		}
+		_, err = val.Bool()
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+}
diff --git a/internal/encoding/json/decode.go b/internal/encoding/json/decode.go
index ddf3a68..a9262b8 100644
--- a/internal/encoding/json/decode.go
+++ b/internal/encoding/json/decode.go
@@ -163,7 +163,7 @@
 
 	in := d.in
 	if len(in) == 0 {
-		return d.newValue(EOF, nil, nil), 0, nil
+		return d.newValue(nil, EOF), 0, nil
 	}
 
 	switch in[0] {
@@ -174,11 +174,11 @@
 		}
 		switch in[0] {
 		case 'n':
-			return d.newValue(Null, in[:n], nil), n, nil
+			return d.newValue(in[:n], Null), n, nil
 		case 't':
-			return d.newValue(Bool, in[:n], true), n, nil
+			return d.newBoolValue(in[:n], true), n, nil
 		case 'f':
-			return d.newValue(Bool, in[:n], false), n, nil
+			return d.newBoolValue(in[:n], false), n, nil
 		}
 
 	case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
@@ -186,7 +186,7 @@
 		if !ok {
 			return Value{}, 0, d.newSyntaxError("invalid number %s", errRegexp.Find(in))
 		}
-		return d.newValue(Number, in[:n], nil), n, nil
+		return d.newValue(in[:n], Number), n, nil
 
 	case '"':
 		var nerr errors.NonFatal
@@ -194,22 +194,22 @@
 		if !nerr.Merge(err) {
 			return Value{}, 0, err
 		}
-		return d.newValue(String, in[:n], s), n, nerr.E
+		return d.newStringValue(in[:n], s), n, nerr.E
 
 	case '{':
-		return d.newValue(StartObject, in[:1], nil), 1, nil
+		return d.newValue(in[:1], StartObject), 1, nil
 
 	case '}':
-		return d.newValue(EndObject, in[:1], nil), 1, nil
+		return d.newValue(in[:1], EndObject), 1, nil
 
 	case '[':
-		return d.newValue(StartArray, in[:1], nil), 1, nil
+		return d.newValue(in[:1], StartArray), 1, nil
 
 	case ']':
-		return d.newValue(EndArray, in[:1], nil), 1, nil
+		return d.newValue(in[:1], EndArray), 1, nil
 
 	case ',':
-		return d.newValue(comma, in[:1], nil), 1, nil
+		return d.newValue(in[:1], comma), 1, nil
 	}
 	return Value{}, 0, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
 }
@@ -288,30 +288,53 @@
 		d.value.typ, start))
 }
 
-// newValue constructs a Value.
-func (d *Decoder) newValue(typ Type, input []byte, value interface{}) Value {
+// newValue constructs a Value for given Type.
+func (d *Decoder) newValue(input []byte, typ Type) Value {
 	line, column := d.position()
 	return Value{
 		input:  input,
 		line:   line,
 		column: column,
 		typ:    typ,
-		value:  value,
+	}
+}
+
+// newBoolValue constructs a Value for a JSON boolean.
+func (d *Decoder) newBoolValue(input []byte, b bool) Value {
+	line, column := d.position()
+	return Value{
+		input:  input,
+		line:   line,
+		column: column,
+		typ:    Bool,
+		boo:    b,
+	}
+}
+
+// newStringValue constructs a Value for a JSON string.
+func (d *Decoder) newStringValue(input []byte, s string) Value {
+	line, column := d.position()
+	return Value{
+		input:  input,
+		line:   line,
+		column: column,
+		typ:    String,
+		str:    s,
 	}
 }
 
 // Value contains a JSON type and value parsed from calling Decoder.Read.
+// For JSON boolean and string, it holds the converted value in boo and str
+// fields respectively. For JSON number, input field holds a valid number which
+// is converted only in Int or Float. Other JSON types do not require any
+// additional data.
 type Value struct {
 	input  []byte
 	line   int
 	column int
 	typ    Type
-	// value will be set to the following Go type based on the type field:
-	//    Bool   => bool
-	//    String => string
-	//    Name   => string
-	// It will be nil if none of the above.
-	value interface{}
+	boo    bool
+	str    string
 }
 
 func (v Value) newError(f string, x ...interface{}) error {
@@ -334,7 +357,7 @@
 	if v.typ != Bool {
 		return false, v.newError("%s is not a bool", v.input)
 	}
-	return v.value.(bool), nil
+	return v.boo, nil
 }
 
 // String returns the string value for a JSON string token or the read value in
@@ -343,7 +366,7 @@
 	if v.typ != String {
 		return string(v.input)
 	}
-	return v.value.(string)
+	return v.str
 }
 
 // Name returns the object name if token is Name, else it will return an error.
@@ -351,7 +374,7 @@
 	if v.typ != Name {
 		return "", v.newError("%s is not an object name", v.input)
 	}
-	return v.value.(string), nil
+	return v.str, nil
 }
 
 // Float returns the floating-point number if token is Number, else it will