encoding/protojson: refactor to follow prototext pattern

All unmarshaling error messages now contain line number and column
information, except for the following errors:
- `unexpected EOF`
- `no support for proto1 MessageSets`
- `required fields X not set`

Changes to internal/encoding/json:
- Moved encoding funcs in string.go and number.go into encode.go.
- Separated out encoding kind constants from decoding ones.
- Renamed file string.go to decode_string.go.
- Renamed file number.go to decode_number.go.
- Renamed Type struct to Kind.
- Renamed Value struct to Token.
- Token accessor methods no longer return error.
  Name, Bool, ParsedString will panic if called on the wrong kind.
  Float, Int, Uint has ok bool result to check against.
- Changed Peek to return Token and error.

Changes to encoding/protojson:
- Updated internal/encoding/json API calls.
- Added line info on most unmarshaling error messages and kept
  description simple and consistent.

Change-Id: Ie50456694f2214c5c4fafd2c9b9239680da0deec
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/218978
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/encoding/json/bench_test.go b/internal/encoding/json/bench_test.go
index db7d761..bee8545 100644
--- a/internal/encoding/json/bench_test.go
+++ b/internal/encoding/json/bench_test.go
@@ -18,9 +18,8 @@
 		if err != nil {
 			b.Fatal(err)
 		}
-		_, err = val.Float(64)
-		if err != nil {
-			b.Fatal(err)
+		if _, ok := val.Float(64); !ok {
+			b.Fatal("not a flaot")
 		}
 	}
 }
@@ -33,9 +32,8 @@
 		if err != nil {
 			b.Fatal(err)
 		}
-		_, err = val.Int(64)
-		if err != nil {
-			b.Fatal(err)
+		if _, ok := val.Int(64); !ok {
+			b.Fatal("not an int64")
 		}
 	}
 }
@@ -48,7 +46,7 @@
 		if err != nil {
 			b.Fatal(err)
 		}
-		_ = val.String()
+		_ = val.ParsedString()
 	}
 }
 
@@ -60,9 +58,6 @@
 		if err != nil {
 			b.Fatal(err)
 		}
-		_, err = val.Bool()
-		if err != nil {
-			b.Fatal(err)
-		}
+		_ = val.Bool()
 	}
 }
diff --git a/internal/encoding/json/decode.go b/internal/encoding/json/decode.go
index 3e58d06..b13fd29 100644
--- a/internal/encoding/json/decode.go
+++ b/internal/encoding/json/decode.go
@@ -9,7 +9,6 @@
 	"fmt"
 	"io"
 	"regexp"
-	"strconv"
 	"unicode/utf8"
 
 	"google.golang.org/protobuf/internal/errors"
@@ -23,22 +22,27 @@
 	peekCall
 )
 
+const unexpectedFmt = "unexpected token %s"
+
+// ErrUnexpectedEOF means that EOF was encountered in the middle of the input.
+var ErrUnexpectedEOF = errors.New("%v", io.ErrUnexpectedEOF)
+
 // Decoder is a token-based JSON decoder.
 type Decoder struct {
 	// lastCall is last method called, either readCall or peekCall.
 	// Initial value is readCall.
 	lastCall call
 
-	// value contains the last read value.
-	value Value
+	// lastToken contains the last read token.
+	lastToken Token
 
-	// err contains the last read error.
-	err error
+	// lastErr contains the last read error.
+	lastErr error
 
-	// startStack is a stack containing StartObject and StartArray types. The
+	// openStack is a stack containing ObjectOpen and ArrayOpen values. The
 	// top of stack represents the object or the array the current value is
 	// directly located in.
-	startStack []Type
+	openStack []Kind
 
 	// orig is used in reporting line and column.
 	orig []byte
@@ -51,193 +55,188 @@
 	return &Decoder{orig: b, in: b}
 }
 
-// Peek looks ahead and returns the next JSON type without advancing a read.
-func (d *Decoder) Peek() Type {
+// Peek looks ahead and returns the next token kind without advancing a read.
+func (d *Decoder) Peek() (Token, error) {
 	defer func() { d.lastCall = peekCall }()
 	if d.lastCall == readCall {
-		d.value, d.err = d.Read()
+		d.lastToken, d.lastErr = d.Read()
 	}
-	return d.value.typ
+	return d.lastToken, d.lastErr
 }
 
-// Read returns the next JSON value. It will return an error if there is no
-// valid value. For String types containing invalid UTF8 characters, a non-fatal
-// error is returned and caller can call Read for the next value.
-func (d *Decoder) Read() (Value, error) {
+// Read returns the next JSON token.
+// It will return an error if there is no valid token.
+func (d *Decoder) Read() (Token, error) {
+	const scalar = Null | Bool | Number | String
+
 	defer func() { d.lastCall = readCall }()
 	if d.lastCall == peekCall {
-		return d.value, d.err
+		return d.lastToken, d.lastErr
 	}
 
-	value, err := d.parseNext()
+	tok, err := d.parseNext()
 	if err != nil {
-		return Value{}, err
+		return Token{}, err
 	}
-	n := value.size
 
-	switch value.typ {
+	switch tok.kind {
 	case EOF:
-		if len(d.startStack) != 0 ||
-			d.value.typ&Null|Bool|Number|String|EndObject|EndArray == 0 {
-			return Value{}, io.ErrUnexpectedEOF
+		if len(d.openStack) != 0 ||
+			d.lastToken.kind&scalar|ObjectClose|ArrayClose == 0 {
+			return Token{}, ErrUnexpectedEOF
 		}
 
 	case Null:
 		if !d.isValueNext() {
-			return Value{}, d.newSyntaxError("unexpected value null")
+			return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString())
 		}
 
 	case Bool, Number:
 		if !d.isValueNext() {
-			return Value{}, d.newSyntaxError("unexpected value %v", value.Raw())
+			return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString())
 		}
 
 	case String:
 		if d.isValueNext() {
 			break
 		}
-		// Check if this is for an object name.
-		if d.value.typ&(StartObject|comma) == 0 {
-			return Value{}, d.newSyntaxError("unexpected value %v", value.Raw())
+		// This string token should only be for a field name.
+		if d.lastToken.kind&(ObjectOpen|comma) == 0 {
+			return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString())
 		}
-		d.in = d.in[n:]
-		d.consume(0)
 		if len(d.in) == 0 {
-			return Value{}, d.newSyntaxError(`unexpected EOF, missing ":" after object name`)
+			return Token{}, ErrUnexpectedEOF
 		}
 		if c := d.in[0]; c != ':' {
-			return Value{}, d.newSyntaxError(`unexpected character %v, missing ":" after object name`, string(c))
+			return Token{}, d.newSyntaxError(d.currPos(), `unexpected character %s, missing ":" after field name`, string(c))
 		}
-		n = 1
-		value.typ = Name
+		tok.kind = Name
+		d.consume(1)
 
-	case StartObject, StartArray:
+	case ObjectOpen, ArrayOpen:
 		if !d.isValueNext() {
-			return Value{}, d.newSyntaxError("unexpected character %v", value.Raw())
+			return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString())
 		}
-		d.startStack = append(d.startStack, value.typ)
+		d.openStack = append(d.openStack, tok.kind)
 
-	case EndObject:
-		if len(d.startStack) == 0 ||
-			d.value.typ == comma ||
-			d.startStack[len(d.startStack)-1] != StartObject {
-			return Value{}, d.newSyntaxError("unexpected character }")
+	case ObjectClose:
+		if len(d.openStack) == 0 ||
+			d.lastToken.kind == comma ||
+			d.openStack[len(d.openStack)-1] != ObjectOpen {
+			return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString())
 		}
-		d.startStack = d.startStack[:len(d.startStack)-1]
+		d.openStack = d.openStack[:len(d.openStack)-1]
 
-	case EndArray:
-		if len(d.startStack) == 0 ||
-			d.value.typ == comma ||
-			d.startStack[len(d.startStack)-1] != StartArray {
-			return Value{}, d.newSyntaxError("unexpected character ]")
+	case ArrayClose:
+		if len(d.openStack) == 0 ||
+			d.lastToken.kind == comma ||
+			d.openStack[len(d.openStack)-1] != ArrayOpen {
+			return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString())
 		}
-		d.startStack = d.startStack[:len(d.startStack)-1]
+		d.openStack = d.openStack[:len(d.openStack)-1]
 
 	case comma:
-		if len(d.startStack) == 0 ||
-			d.value.typ&(Null|Bool|Number|String|EndObject|EndArray) == 0 {
-			return Value{}, d.newSyntaxError("unexpected character ,")
+		if len(d.openStack) == 0 ||
+			d.lastToken.kind&(scalar|ObjectClose|ArrayClose) == 0 {
+			return Token{}, d.newSyntaxError(tok.pos, unexpectedFmt, tok.RawString())
 		}
 	}
 
-	// Update d.value only after validating value to be in the right sequence.
-	d.value = value
-	d.in = d.in[n:]
+	// Update d.lastToken only after validating token to be in the right sequence.
+	d.lastToken = tok
 
-	if d.value.typ == comma {
+	if d.lastToken.kind == comma {
 		return d.Read()
 	}
-	return value, nil
+	return tok, nil
 }
 
 // Any sequence that looks like a non-delimiter (for error reporting).
 var errRegexp = regexp.MustCompile(`^([-+._a-zA-Z0-9]{1,32}|.)`)
 
-// parseNext parses for the next JSON value. It returns a Value object for
-// different types, except for Name. It does not handle whether the next value
+// parseNext parses for the next JSON token. It returns a Token object for
+// different types, except for Name. It does not handle whether the next token
 // is in a valid sequence or not.
-func (d *Decoder) parseNext() (value Value, err error) {
+func (d *Decoder) parseNext() (Token, error) {
 	// Trim leading spaces.
 	d.consume(0)
 
 	in := d.in
 	if len(in) == 0 {
-		return d.newValue(EOF, nil, 0), nil
+		return d.consumeToken(EOF, 0), nil
 	}
 
 	switch in[0] {
 	case 'n':
-		n := matchWithDelim("null", in)
-		if n == 0 {
-			return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
+		if n := matchWithDelim("null", in); n != 0 {
+			return d.consumeToken(Null, n), nil
 		}
-		return d.newValue(Null, in, n), nil
 
 	case 't':
-		n := matchWithDelim("true", in)
-		if n == 0 {
-			return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
+		if n := matchWithDelim("true", in); n != 0 {
+			return d.consumeBoolToken(true, n), nil
 		}
-		return d.newBoolValue(in, n, true), nil
 
 	case 'f':
-		n := matchWithDelim("false", in)
-		if n == 0 {
-			return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
+		if n := matchWithDelim("false", in); n != 0 {
+			return d.consumeBoolToken(false, n), nil
 		}
-		return d.newBoolValue(in, n, false), nil
 
 	case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-		n, ok := consumeNumber(in)
-		if !ok {
-			return Value{}, d.newSyntaxError("invalid number %s", errRegexp.Find(in))
+		if n, ok := parseNumber(in); ok {
+			return d.consumeToken(Number, n), nil
 		}
-		return d.newValue(Number, in, n), nil
 
 	case '"':
 		s, n, err := d.parseString(in)
 		if err != nil {
-			return Value{}, err
+			return Token{}, err
 		}
-		return d.newStringValue(in, n, s), nil
+		return d.consumeStringToken(s, n), nil
 
 	case '{':
-		return d.newValue(StartObject, in, 1), nil
+		return d.consumeToken(ObjectOpen, 1), nil
 
 	case '}':
-		return d.newValue(EndObject, in, 1), nil
+		return d.consumeToken(ObjectClose, 1), nil
 
 	case '[':
-		return d.newValue(StartArray, in, 1), nil
+		return d.consumeToken(ArrayOpen, 1), nil
 
 	case ']':
-		return d.newValue(EndArray, in, 1), nil
+		return d.consumeToken(ArrayClose, 1), nil
 
 	case ',':
-		return d.newValue(comma, in, 1), nil
+		return d.consumeToken(comma, 1), nil
 	}
-	return Value{}, d.newSyntaxError("invalid value %s", errRegexp.Find(in))
-}
-
-// position returns line and column number of index in given orig slice.
-func position(orig []byte, idx int) (int, int) {
-	b := orig[:idx]
-	line := bytes.Count(b, []byte("\n")) + 1
-	if i := bytes.LastIndexByte(b, '\n'); i >= 0 {
-		b = b[i+1:]
-	}
-	column := utf8.RuneCount(b) + 1 // ignore multi-rune characters
-	return line, column
+	return Token{}, d.newSyntaxError(d.currPos(), "invalid value %s", errRegexp.Find(in))
 }
 
 // newSyntaxError returns an error with line and column information useful for
 // syntax errors.
-func (d *Decoder) newSyntaxError(f string, x ...interface{}) error {
+func (d *Decoder) newSyntaxError(pos int, f string, x ...interface{}) error {
 	e := errors.New(f, x...)
-	line, column := position(d.orig, len(d.orig)-len(d.in))
+	line, column := d.Position(pos)
 	return errors.New("syntax error (line %d:%d): %v", line, column, e)
 }
 
+// Position returns line and column number of given index of the original input.
+// It will panic if index is out of range.
+func (d *Decoder) Position(idx int) (line int, column int) {
+	b := d.orig[:idx]
+	line = bytes.Count(b, []byte("\n")) + 1
+	if i := bytes.LastIndexByte(b, '\n'); i >= 0 {
+		b = b[i+1:]
+	}
+	column = utf8.RuneCount(b) + 1 // ignore multi-rune characters
+	return line, column
+}
+
+// currPos returns the current index position of d.in from d.orig.
+func (d *Decoder) currPos() int {
+	return len(d.orig) - len(d.in)
+}
+
 // matchWithDelim matches s with the input b and verifies that the match
 // terminates with a delimiter of some form (e.g., r"[^-+_.a-zA-Z0-9]").
 // As a special case, EOF is considered a delimiter. It returns the length of s
@@ -278,193 +277,64 @@
 // isValueNext returns true if next type should be a JSON value: Null,
 // Number, String or Bool.
 func (d *Decoder) isValueNext() bool {
-	if len(d.startStack) == 0 {
-		return d.value.typ == 0
+	if len(d.openStack) == 0 {
+		return d.lastToken.kind == 0
 	}
 
-	start := d.startStack[len(d.startStack)-1]
+	start := d.openStack[len(d.openStack)-1]
 	switch start {
-	case StartObject:
-		return d.value.typ&Name != 0
-	case StartArray:
-		return d.value.typ&(StartArray|comma) != 0
+	case ObjectOpen:
+		return d.lastToken.kind&Name != 0
+	case ArrayOpen:
+		return d.lastToken.kind&(ArrayOpen|comma) != 0
 	}
 	panic(fmt.Sprintf(
-		"unreachable logic in Decoder.isValueNext, lastType: %v, startStack: %v",
-		d.value.typ, start))
+		"unreachable logic in Decoder.isValueNext, lastToken.kind: %v, openStack: %v",
+		d.lastToken.kind, start))
 }
 
-// newValue constructs a Value for given Type.
-func (d *Decoder) newValue(typ Type, input []byte, size int) Value {
-	return Value{
-		typ:   typ,
-		input: d.orig,
-		start: len(d.orig) - len(input),
-		size:  size,
+// consumeToken constructs a Token for given Kind with raw value derived from
+// current d.in and given size, and consumes the given size-lenght of it.
+func (d *Decoder) consumeToken(kind Kind, size int) Token {
+	tok := Token{
+		kind: kind,
+		raw:  d.in[:size],
+		pos:  len(d.orig) - len(d.in),
 	}
+	d.consume(size)
+	return tok
 }
 
-// newBoolValue constructs a Value for a JSON boolean.
-func (d *Decoder) newBoolValue(input []byte, size int, b bool) Value {
-	return Value{
-		typ:   Bool,
-		input: d.orig,
-		start: len(d.orig) - len(input),
-		size:  size,
-		boo:   b,
+// consumeBoolToken constructs a Token for a Bool kind with raw value derived from
+// current d.in and given size.
+func (d *Decoder) consumeBoolToken(b bool, size int) Token {
+	tok := Token{
+		kind: Bool,
+		raw:  d.in[:size],
+		pos:  len(d.orig) - len(d.in),
+		boo:  b,
 	}
+	d.consume(size)
+	return tok
 }
 
-// newStringValue constructs a Value for a JSON string.
-func (d *Decoder) newStringValue(input []byte, size int, s string) Value {
-	return Value{
-		typ:   String,
-		input: d.orig,
-		start: len(d.orig) - len(input),
-		size:  size,
-		str:   s,
+// consumeStringToken constructs a Token for a String kind with raw value derived
+// from current d.in and given size.
+func (d *Decoder) consumeStringToken(s string, size int) Token {
+	tok := Token{
+		kind: String,
+		raw:  d.in[:size],
+		pos:  len(d.orig) - len(d.in),
+		str:  s,
 	}
+	d.consume(size)
+	return tok
 }
 
 // Clone returns a copy of the Decoder for use in reading ahead the next JSON
 // object, array or other values without affecting current Decoder.
 func (d *Decoder) Clone() *Decoder {
 	ret := *d
-	ret.startStack = append([]Type(nil), ret.startStack...)
+	ret.openStack = append([]Kind(nil), ret.openStack...)
 	return &ret
 }
-
-// Value provides a parsed JSON type and value.
-//
-// The original input slice is stored in this struct in order to compute for
-// position as needed. The raw JSON value is derived from the original input
-// slice given start and size.
-//
-// For JSON boolean and string, it holds the converted value in boo and str
-// fields respectively. For JSON number, the raw JSON value holds a valid number
-// which is converted only in Int or Float. Other JSON types do not require any
-// additional data.
-type Value struct {
-	typ   Type
-	input []byte
-	start int
-	size  int
-	boo   bool
-	str   string
-}
-
-func (v Value) newError(f string, x ...interface{}) error {
-	e := errors.New(f, x...)
-	line, col := v.Position()
-	return errors.New("error (line %d:%d): %v", line, col, e)
-}
-
-// Type returns the JSON type.
-func (v Value) Type() Type {
-	return v.typ
-}
-
-// Position returns the line and column of the value.
-func (v Value) Position() (int, int) {
-	return position(v.input, v.start)
-}
-
-// Bool returns the bool value if token is Bool, else it will return an error.
-func (v Value) Bool() (bool, error) {
-	if v.typ != Bool {
-		return false, v.newError("%s is not a bool", v.Raw())
-	}
-	return v.boo, nil
-}
-
-// String returns the string value for a JSON string token or the read value in
-// string if token is not a string.
-func (v Value) String() string {
-	if v.typ != String {
-		return v.Raw()
-	}
-	return v.str
-}
-
-// Name returns the object name if token is Name, else it will return an error.
-func (v Value) Name() (string, error) {
-	if v.typ != Name {
-		return "", v.newError("%s is not an object name", v.Raw())
-	}
-	return v.str, nil
-}
-
-// Raw returns the read value in string.
-func (v Value) Raw() string {
-	return string(v.input[v.start : v.start+v.size])
-}
-
-// Float returns the floating-point number if token is Number, else it will
-// return an error.
-//
-// The floating-point precision is specified by the bitSize parameter: 32 for
-// float32 or 64 for float64. If bitSize=32, the result still has type float64,
-// but it will be convertible to float32 without changing its value. It will
-// return an error if the number exceeds the floating point limits for given
-// bitSize.
-func (v Value) Float(bitSize int) (float64, error) {
-	if v.typ != Number {
-		return 0, v.newError("%s is not a number", v.Raw())
-	}
-	f, err := strconv.ParseFloat(v.Raw(), bitSize)
-	if err != nil {
-		return 0, v.newError("%v", err)
-	}
-	return f, nil
-}
-
-// Int returns the signed integer number if token is Number, else it will
-// return an error.
-//
-// The given bitSize specifies the integer type that the result must fit into.
-// It returns an error if the number is not an integer value or if the result
-// exceeds the limits for given bitSize.
-func (v Value) Int(bitSize int) (int64, error) {
-	s, err := v.getIntStr()
-	if err != nil {
-		return 0, err
-	}
-	n, err := strconv.ParseInt(s, 10, bitSize)
-	if err != nil {
-		return 0, v.newError("%v", err)
-	}
-	return n, nil
-}
-
-// Uint returns the signed integer number if token is Number, else it will
-// return an error.
-//
-// The given bitSize specifies the unsigned integer type that the result must
-// fit into.  It returns an error if the number is not an unsigned integer value
-// or if the result exceeds the limits for given bitSize.
-func (v Value) Uint(bitSize int) (uint64, error) {
-	s, err := v.getIntStr()
-	if err != nil {
-		return 0, err
-	}
-	n, err := strconv.ParseUint(s, 10, bitSize)
-	if err != nil {
-		return 0, v.newError("%v", err)
-	}
-	return n, nil
-}
-
-func (v Value) getIntStr() (string, error) {
-	if v.typ != Number {
-		return "", v.newError("%s is not a number", v.input)
-	}
-	parts, ok := parseNumber(v.input[v.start : v.start+v.size])
-	if !ok {
-		return "", v.newError("%s is not a number", v.input)
-	}
-	num, ok := normalizeToIntString(parts)
-	if !ok {
-		return "", v.newError("cannot convert %s to integer", v.input)
-	}
-	return num, nil
-}
diff --git a/internal/encoding/json/number.go b/internal/encoding/json/decode_number.go
similarity index 83%
rename from internal/encoding/json/number.go
rename to internal/encoding/json/decode_number.go
index 529331f..a695689 100644
--- a/internal/encoding/json/number.go
+++ b/internal/encoding/json/decode_number.go
@@ -6,46 +6,14 @@
 
 import (
 	"bytes"
-	"math"
 	"strconv"
 )
 
-// appendFloat formats given float in bitSize, and appends to the given []byte.
-func appendFloat(out []byte, n float64, bitSize int) []byte {
-	switch {
-	case math.IsNaN(n):
-		return append(out, `"NaN"`...)
-	case math.IsInf(n, +1):
-		return append(out, `"Infinity"`...)
-	case math.IsInf(n, -1):
-		return append(out, `"-Infinity"`...)
-	}
-
-	// JSON number formatting logic based on encoding/json.
-	// See floatEncoder.encode for reference.
-	fmt := byte('f')
-	if abs := math.Abs(n); abs != 0 {
-		if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) ||
-			bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
-			fmt = 'e'
-		}
-	}
-	out = strconv.AppendFloat(out, n, fmt, -1, bitSize)
-	if fmt == 'e' {
-		n := len(out)
-		if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' {
-			out[n-2] = out[n-1]
-			out = out[:n-1]
-		}
-	}
-	return out
-}
-
-// consumeNumber reads the given []byte for a valid JSON number. If it is valid,
+// parseNumber reads the given []byte for a valid JSON number. If it is valid,
 // it returns the number of bytes.  Parsing logic follows the definition in
 // https://tools.ietf.org/html/rfc7159#section-6, and is based off
 // encoding/json.isValidNumber function.
-func consumeNumber(input []byte) (int, bool) {
+func parseNumber(input []byte) (int, bool) {
 	var n int
 
 	s := input
@@ -128,7 +96,7 @@
 // parseNumber constructs numberParts from given []byte. The logic here is
 // similar to consumeNumber above with the difference of having to construct
 // numberParts. The slice fields in numberParts are subslices of the input.
-func parseNumber(input []byte) (numberParts, bool) {
+func parseNumberParts(input []byte) (numberParts, bool) {
 	var neg bool
 	var intp []byte
 	var frac []byte
diff --git a/internal/encoding/json/decode_string.go b/internal/encoding/json/decode_string.go
new file mode 100644
index 0000000..f7fea7d
--- /dev/null
+++ b/internal/encoding/json/decode_string.go
@@ -0,0 +1,91 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json
+
+import (
+	"strconv"
+	"unicode"
+	"unicode/utf16"
+	"unicode/utf8"
+
+	"google.golang.org/protobuf/internal/strs"
+)
+
+func (d *Decoder) parseString(in []byte) (string, int, error) {
+	in0 := in
+	if len(in) == 0 {
+		return "", 0, ErrUnexpectedEOF
+	}
+	if in[0] != '"' {
+		return "", 0, d.newSyntaxError(d.currPos(), "invalid character %q at start of string", in[0])
+	}
+	in = in[1:]
+	i := indexNeedEscapeInBytes(in)
+	in, out := in[i:], in[:i:i] // set cap to prevent mutations
+	for len(in) > 0 {
+		switch r, n := utf8.DecodeRune(in); {
+		case r == utf8.RuneError && n == 1:
+			return "", 0, d.newSyntaxError(d.currPos(), "invalid UTF-8 in string")
+		case r < ' ':
+			return "", 0, d.newSyntaxError(d.currPos(), "invalid character %q in string", r)
+		case r == '"':
+			in = in[1:]
+			n := len(in0) - len(in)
+			return string(out), n, nil
+		case r == '\\':
+			if len(in) < 2 {
+				return "", 0, ErrUnexpectedEOF
+			}
+			switch r := in[1]; r {
+			case '"', '\\', '/':
+				in, out = in[2:], append(out, r)
+			case 'b':
+				in, out = in[2:], append(out, '\b')
+			case 'f':
+				in, out = in[2:], append(out, '\f')
+			case 'n':
+				in, out = in[2:], append(out, '\n')
+			case 'r':
+				in, out = in[2:], append(out, '\r')
+			case 't':
+				in, out = in[2:], append(out, '\t')
+			case 'u':
+				if len(in) < 6 {
+					return "", 0, ErrUnexpectedEOF
+				}
+				v, err := strconv.ParseUint(string(in[2:6]), 16, 16)
+				if err != nil {
+					return "", 0, d.newSyntaxError(d.currPos(), "invalid escape code %q in string", in[:6])
+				}
+				in = in[6:]
+
+				r := rune(v)
+				if utf16.IsSurrogate(r) {
+					if len(in) < 6 {
+						return "", 0, ErrUnexpectedEOF
+					}
+					v, err := strconv.ParseUint(string(in[2:6]), 16, 16)
+					r = utf16.DecodeRune(r, rune(v))
+					if in[0] != '\\' || in[1] != 'u' ||
+						r == unicode.ReplacementChar || err != nil {
+						return "", 0, d.newSyntaxError(d.currPos(), "invalid escape code %q in string", in[:6])
+					}
+					in = in[6:]
+				}
+				out = append(out, string(r)...)
+			default:
+				return "", 0, d.newSyntaxError(d.currPos(), "invalid escape code %q in string", in[:2])
+			}
+		default:
+			i := indexNeedEscapeInBytes(in[n:])
+			in, out = in[n+i:], append(out, in[:n+i]...)
+		}
+	}
+	return "", 0, ErrUnexpectedEOF
+}
+
+// indexNeedEscapeInBytes returns the index of the character that needs
+// escaping. If no characters need escaping, this returns the input length.
+func indexNeedEscapeInBytes(b []byte) int { return indexNeedEscapeInString(strs.UnsafeString(b)) }
diff --git a/internal/encoding/json/decode_test.go b/internal/encoding/json/decode_test.go
index f4905d0..b788c39 100644
--- a/internal/encoding/json/decode_test.go
+++ b/internal/encoding/json/decode_test.go
@@ -5,84 +5,374 @@
 package json_test
 
 import (
+	"fmt"
 	"math"
 	"strings"
 	"testing"
 	"unicode/utf8"
 
+	"github.com/google/go-cmp/cmp"
 	"google.golang.org/protobuf/internal/encoding/json"
 )
 
 type R struct {
-	// T is expected Type returned from calling Decoder.Read.
-	T json.Type
 	// E is expected error substring from calling Decoder.Read if set.
 	E string
-	// V is expected value from calling
-	// Value.{Bool()|Float()|Int()|Uint()|String()} depending on type.
-	V interface{}
-	// VE is expected error substring from calling
-	// Value.{Bool()|Float()|Int()|Uint()|String()} depending on type if set.
-	VE string
+	// V is one of the checker implementations that validates the token value.
+	V checker
+	// P is expected Token.Pos() if set > 0.
+	P int
+	// RS is expected result from Token.RawString() if not empty.
+	RS string
 }
 
+// checker defines API for Token validation.
+type checker interface {
+	// check checks and expects for token API call to return and compare
+	// against implementation-stored value. Returns empty string if success,
+	// else returns error message describing the error.
+	check(json.Token) string
+}
+
+// checkers that checks the token kind only.
+var (
+	EOF         = kindOnly{json.EOF}
+	Null        = kindOnly{json.Null}
+	ObjectOpen  = kindOnly{json.ObjectOpen}
+	ObjectClose = kindOnly{json.ObjectClose}
+	ArrayOpen   = kindOnly{json.ArrayOpen}
+	ArrayClose  = kindOnly{json.ArrayClose}
+)
+
+type kindOnly struct {
+	want json.Kind
+}
+
+func (x kindOnly) check(tok json.Token) string {
+	if got := tok.Kind(); got != x.want {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, x.want)
+	}
+	return ""
+}
+
+type Name struct {
+	val string
+}
+
+func (x Name) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Name {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Name)
+	}
+
+	if got := tok.Name(); got != x.val {
+		return fmt.Sprintf("Token.Name(): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+type Bool struct {
+	val bool
+}
+
+func (x Bool) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Bool {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Bool)
+	}
+
+	if got := tok.Bool(); got != x.val {
+		return fmt.Sprintf("Token.Bool(): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+type Str struct {
+	val string
+}
+
+func (x Str) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.String {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.String)
+	}
+
+	if got := tok.ParsedString(); got != x.val {
+		return fmt.Sprintf("Token.ParsedString(): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+type F64 struct {
+	val float64
+}
+
+func (x F64) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	got, ok := tok.Float(64)
+	if !ok {
+		return fmt.Sprintf("Token.Float(64): returned not ok")
+	}
+	if got != x.val {
+		return fmt.Sprintf("Token.Float(64): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+type F32 struct {
+	val float32
+}
+
+func (x F32) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	got, ok := tok.Float(32)
+	if !ok {
+		return fmt.Sprintf("Token.Float(32): returned not ok")
+	}
+	if float32(got) != x.val {
+		return fmt.Sprintf("Token.Float(32): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+// NotF64 is a checker to validate a Number token where Token.Float(64) returns not ok.
+var NotF64 = xf64{}
+
+type xf64 struct{}
+
+func (x xf64) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	_, ok := tok.Float(64)
+	if ok {
+		return fmt.Sprintf("Token.Float(64): returned ok")
+	}
+	return ""
+}
+
+// NotF32 is a checker to validate a Number token where Token.Float(32) returns not ok.
+var NotF32 = xf32{}
+
+type xf32 struct{}
+
+func (x xf32) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	_, ok := tok.Float(32)
+	if ok {
+		return fmt.Sprintf("Token.Float(32): returned ok")
+	}
+	return ""
+}
+
+type I64 struct {
+	val int64
+}
+
+func (x I64) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	got, ok := tok.Int(64)
+	if !ok {
+		return fmt.Sprintf("Token.Int(64): returned not ok")
+	}
+	if got != x.val {
+		return fmt.Sprintf("Token.Int(64): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+type I32 struct {
+	val int32
+}
+
+func (x I32) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	got, ok := tok.Int(32)
+	if !ok {
+		return fmt.Sprintf("Token.Int(32): returned not ok")
+	}
+	if int32(got) != x.val {
+		return fmt.Sprintf("Token.Int(32): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+// NotI64 is a checker to validate a Number token where Token.Int(64) returns not ok.
+var NotI64 = xi64{}
+
+type xi64 struct{}
+
+func (x xi64) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	_, ok := tok.Int(64)
+	if ok {
+		return fmt.Sprintf("Token.Int(64): returned ok")
+	}
+	return ""
+}
+
+// NotI32 is a checker to validate a Number token where Token.Int(32) returns not ok.
+var NotI32 = xi32{}
+
+type xi32 struct{}
+
+func (x xi32) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	_, ok := tok.Int(32)
+	if ok {
+		return fmt.Sprintf("Token.Int(32): returned ok")
+	}
+	return ""
+}
+
+type Ui64 struct {
+	val uint64
+}
+
+func (x Ui64) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	got, ok := tok.Uint(64)
+	if !ok {
+		return fmt.Sprintf("Token.Uint(64): returned not ok")
+	}
+	if got != x.val {
+		return fmt.Sprintf("Token.Uint(64): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+type Ui32 struct {
+	val uint32
+}
+
+func (x Ui32) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	got, ok := tok.Uint(32)
+	if !ok {
+		return fmt.Sprintf("Token.Uint(32): returned not ok")
+	}
+	if uint32(got) != x.val {
+		return fmt.Sprintf("Token.Uint(32): got %v, want %v", got, x.val)
+	}
+	return ""
+}
+
+// NotUi64 is a checker to validate a Number token where Token.Uint(64) returns not ok.
+var NotUi64 = xui64{}
+
+type xui64 struct{}
+
+func (x xui64) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	_, ok := tok.Uint(64)
+	if ok {
+		return fmt.Sprintf("Token.Uint(64): returned ok")
+	}
+	return ""
+}
+
+// NotI32 is a checker to validate a Number token where Token.Uint(32) returns not ok.
+var NotUi32 = xui32{}
+
+type xui32 struct{}
+
+func (x xui32) check(tok json.Token) string {
+	if got := tok.Kind(); got != json.Number {
+		return fmt.Sprintf("Token.Kind(): got %v, want %v", got, json.Number)
+	}
+
+	_, ok := tok.Uint(32)
+	if ok {
+		return fmt.Sprintf("Token.Uint(32): returned ok")
+	}
+	return ""
+}
+
+var errEOF = json.ErrUnexpectedEOF.Error()
+
 func TestDecoder(t *testing.T) {
 	const space = " \n\r\t"
 
 	tests := []struct {
-		input string
+		in string
 		// want is a list of expected values returned from calling
 		// Decoder.Read. An item makes the test code invoke
-		// Decoder.Read and compare against R.T and R.E.  For Bool,
-		// Number and String tokens, it invokes the corresponding getter method
-		// and compares the returned value against R.V or R.VE if it returned an
-		// error.
+		// Decoder.Read and compare against R.E for error returned or use R.V to
+		// validate the returned Token object.
 		want []R
 	}{
 		{
-			input: ``,
-			want:  []R{{T: json.EOF}},
+			in:   ``,
+			want: []R{{V: EOF}},
 		},
 		{
-			input: space,
-			want:  []R{{T: json.EOF}},
+			in:   space,
+			want: []R{{V: EOF}},
 		},
 		{
 			// Calling Read after EOF will keep returning EOF for
 			// succeeding Read calls.
-			input: space,
+			in: space,
 			want: []R{
-				{T: json.EOF},
-				{T: json.EOF},
-				{T: json.EOF},
+				{V: EOF},
+				{V: EOF},
+				{V: EOF},
 			},
 		},
 
 		// JSON literals.
 		{
-			input: space + `null` + space,
+			in: space + `null` + space,
 			want: []R{
-				{T: json.Null},
-				{T: json.EOF},
+				{V: Null, P: len(space), RS: `null`},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `true` + space,
+			in: space + `true` + space,
 			want: []R{
-				{T: json.Bool, V: true},
-				{T: json.EOF},
+				{V: Bool{true}},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `false` + space,
+			in: space + `false` + space,
 			want: []R{
-				{T: json.Bool, V: false},
-				{T: json.EOF},
+				{V: Bool{false}},
+				{V: EOF},
 			},
 		},
 		{
 			// Error returned will produce the same error again.
-			input: space + `foo` + space,
+			in: space + `foo` + space,
 			want: []R{
 				{E: `invalid value foo`},
 				{E: `invalid value foo`},
@@ -91,1027 +381,1001 @@
 
 		// JSON strings.
 		{
-			input: space + `""` + space,
+			in: space + `""` + space,
 			want: []R{
-				{T: json.String, V: ""},
-				{T: json.EOF},
+				{V: Str{}},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `"hello"` + space,
+			in: space + `"hello"` + space,
 			want: []R{
-				{T: json.String, V: "hello"},
-				{T: json.EOF},
+				{V: Str{"hello"}, RS: `"hello"`},
+				{V: EOF},
 			},
 		},
 		{
-			input: `"hello`,
-			want:  []R{{E: `unexpected EOF`}},
+			in:   `"hello`,
+			want: []R{{E: errEOF}},
 		},
 		{
-			input: "\"\x00\"",
-			want:  []R{{E: `invalid character '\x00' in string`}},
+			in:   "\"\x00\"",
+			want: []R{{E: `invalid character '\x00' in string`}},
 		},
 		{
-			input: "\"\u0031\u0032\"",
+			in: "\"\u0031\u0032\"",
 			want: []R{
-				{T: json.String, V: "12"},
-				{T: json.EOF},
+				{V: Str{"12"}, RS: "\"\u0031\u0032\""},
+				{V: EOF},
 			},
 		},
 		{
 			// Invalid UTF-8 error is returned in ReadString instead of Read.
-			input: "\"\xff\"",
-			want:  []R{{E: `syntax error (line 1:1): invalid UTF-8 in string`}},
+			in:   "\"\xff\"",
+			want: []R{{E: `syntax error (line 1:1): invalid UTF-8 in string`}},
 		},
 		{
-			input: `"` + string(utf8.RuneError) + `"`,
+			in: `"` + string(utf8.RuneError) + `"`,
 			want: []R{
-				{T: json.String, V: string(utf8.RuneError)},
-				{T: json.EOF},
+				{V: Str{string(utf8.RuneError)}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `"\uFFFD"`,
+			in: `"\uFFFD"`,
 			want: []R{
-				{T: json.String, V: string(utf8.RuneError)},
-				{T: json.EOF},
+				{V: Str{string(utf8.RuneError)}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `"\x"`,
-			want:  []R{{E: `invalid escape code "\\x" in string`}},
+			in:   `"\x"`,
+			want: []R{{E: `invalid escape code "\\x" in string`}},
 		},
 		{
-			input: `"\uXXXX"`,
-			want:  []R{{E: `invalid escape code "\\uXXXX" in string`}},
+			in:   `"\uXXXX"`,
+			want: []R{{E: `invalid escape code "\\uXXXX" in string`}},
 		},
 		{
-			input: `"\uDEAD"`, // unmatched surrogate pair
-			want:  []R{{E: `unexpected EOF`}},
+			in:   `"\uDEAD"`, // unmatched surrogate pair
+			want: []R{{E: errEOF}},
 		},
 		{
-			input: `"\uDEAD\uBEEF"`, // invalid surrogate half
-			want:  []R{{E: `invalid escape code "\\uBEEF" in string`}},
+			in:   `"\uDEAD\uBEEF"`, // invalid surrogate half
+			want: []R{{E: `invalid escape code "\\uBEEF" in string`}},
 		},
 		{
-			input: `"\uD800\udead"`, // valid surrogate pair
+			in: `"\uD800\udead"`, // valid surrogate pair
 			want: []R{
-				{T: json.String, V: `𐊭`},
-				{T: json.EOF},
+				{V: Str{`𐊭`}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `"\u0000\"\\\/\b\f\n\r\t"`,
+			in: `"\u0000\"\\\/\b\f\n\r\t"`,
 			want: []R{
-				{T: json.String, V: "\u0000\"\\/\b\f\n\r\t"},
-				{T: json.EOF},
+				{V: Str{"\u0000\"\\/\b\f\n\r\t"}},
+				{V: EOF},
 			},
 		},
 
 		// Invalid JSON numbers.
 		{
-			input: `-`,
-			want:  []R{{E: `invalid number -`}},
+			in:   `-`,
+			want: []R{{E: `invalid value -`}},
 		},
 		{
-			input: `+0`,
-			want:  []R{{E: `invalid value +0`}},
+			in:   `+0`,
+			want: []R{{E: `invalid value +0`}},
 		},
 		{
-			input: `-+`,
-			want:  []R{{E: `invalid number -+`}},
+			in:   `-+`,
+			want: []R{{E: `invalid value -+`}},
 		},
 		{
-			input: `0.`,
-			want:  []R{{E: `invalid number 0.`}},
+			in:   `0.`,
+			want: []R{{E: `invalid value 0.`}},
 		},
 		{
-			input: `.1`,
-			want:  []R{{E: `invalid value .1`}},
+			in:   `.1`,
+			want: []R{{E: `invalid value .1`}},
 		},
 		{
-			input: `1.0.1`,
-			want:  []R{{E: `invalid number 1.0.1`}},
+			in:   `1.0.1`,
+			want: []R{{E: `invalid value 1.0.1`}},
 		},
 		{
-			input: `1..1`,
-			want:  []R{{E: `invalid number 1..1`}},
+			in:   `1..1`,
+			want: []R{{E: `invalid value 1..1`}},
 		},
 		{
-			input: `-1-2`,
-			want:  []R{{E: `invalid number -1-2`}},
+			in:   `-1-2`,
+			want: []R{{E: `invalid value -1-2`}},
 		},
 		{
-			input: `01`,
-			want:  []R{{E: `invalid number 01`}},
+			in:   `01`,
+			want: []R{{E: `invalid value 01`}},
 		},
 		{
-			input: `1e`,
-			want:  []R{{E: `invalid number 1e`}},
+			in:   `1e`,
+			want: []R{{E: `invalid value 1e`}},
 		},
 		{
-			input: `1e1.2`,
-			want:  []R{{E: `invalid number 1e1.2`}},
+			in:   `1e1.2`,
+			want: []R{{E: `invalid value 1e1.2`}},
 		},
 		{
-			input: `1Ee`,
-			want:  []R{{E: `invalid number 1Ee`}},
+			in:   `1Ee`,
+			want: []R{{E: `invalid value 1Ee`}},
 		},
 		{
-			input: `1.e1`,
-			want:  []R{{E: `invalid number 1.e1`}},
+			in:   `1.e1`,
+			want: []R{{E: `invalid value 1.e1`}},
 		},
 		{
-			input: `1.e+`,
-			want:  []R{{E: `invalid number 1.e+`}},
+			in:   `1.e+`,
+			want: []R{{E: `invalid value 1.e+`}},
 		},
 		{
-			input: `1e+-2`,
-			want:  []R{{E: `invalid number 1e+-2`}},
+			in:   `1e+-2`,
+			want: []R{{E: `invalid value 1e+-2`}},
 		},
 		{
-			input: `1e--2`,
-			want:  []R{{E: `invalid number 1e--2`}},
+			in:   `1e--2`,
+			want: []R{{E: `invalid value 1e--2`}},
 		},
 		{
-			input: `1.0true`,
-			want:  []R{{E: `invalid number 1.0true`}},
+			in:   `1.0true`,
+			want: []R{{E: `invalid value 1.0true`}},
 		},
 
 		// JSON numbers as floating point.
 		{
-			input: space + `0.0` + space,
+			in: space + `0.0` + space,
 			want: []R{
-				{T: json.Number, V: float32(0)},
-				{T: json.EOF},
+				{V: F32{0}, P: len(space), RS: `0.0`},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `0` + space,
+			in: space + `0` + space,
 			want: []R{
-				{T: json.Number, V: float32(0)},
-				{T: json.EOF},
+				{V: F32{0}},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `-0` + space,
+			in: space + `-0` + space,
 			want: []R{
-				{T: json.Number, V: float32(math.Copysign(0, -1))},
-				{T: json.EOF},
+				{V: F32{float32(math.Copysign(0, -1))}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-0`,
+			in: `-0`,
 			want: []R{
-				{T: json.Number, V: math.Copysign(0, -1)},
-				{T: json.EOF},
+				{V: F64{math.Copysign(0, -1)}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-0.0`,
+			in: `-0.0`,
 			want: []R{
-				{T: json.Number, V: float32(math.Copysign(0, -1))},
-				{T: json.EOF},
+				{V: F32{float32(math.Copysign(0, -1))}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-0.0`,
+			in: `-0.0`,
 			want: []R{
-				{T: json.Number, V: math.Copysign(0, -1)},
-				{T: json.EOF},
+				{V: F64{math.Copysign(0, -1)}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1.02`,
+			in: `-1.02`,
 			want: []R{
-				{T: json.Number, V: float32(-1.02)},
-				{T: json.EOF},
+				{V: F32{-1.02}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1.020000`,
+			in: `1.020000`,
 			want: []R{
-				{T: json.Number, V: float32(1.02)},
-				{T: json.EOF},
+				{V: F32{1.02}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1.0e0`,
+			in: `-1.0e0`,
 			want: []R{
-				{T: json.Number, V: float32(-1)},
-				{T: json.EOF},
+				{V: F32{-1}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1.0e-000`,
+			in: `1.0e-000`,
 			want: []R{
-				{T: json.Number, V: float32(1)},
-				{T: json.EOF},
+				{V: F32{1}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1e+00`,
+			in: `1e+00`,
 			want: []R{
-				{T: json.Number, V: float32(1)},
-				{T: json.EOF},
+				{V: F32{1}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1.02e3`,
+			in: `1.02e3`,
 			want: []R{
-				{T: json.Number, V: float32(1.02e3)},
-				{T: json.EOF},
+				{V: F32{1.02e3}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1.02E03`,
+			in: `-1.02E03`,
 			want: []R{
-				{T: json.Number, V: float32(-1.02e3)},
-				{T: json.EOF},
+				{V: F32{-1.02e3}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1.0200e+3`,
+			in: `1.0200e+3`,
 			want: []R{
-				{T: json.Number, V: float32(1.02e3)},
-				{T: json.EOF},
+				{V: F32{1.02e3}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1.0200E+03`,
+			in: `-1.0200E+03`,
 			want: []R{
-				{T: json.Number, V: float32(-1.02e3)},
-				{T: json.EOF},
+				{V: F32{-1.02e3}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1.0200e-3`,
+			in: `1.0200e-3`,
 			want: []R{
-				{T: json.Number, V: float32(1.02e-3)},
-				{T: json.EOF},
+				{V: F32{1.02e-3}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1.0200E-03`,
+			in: `-1.0200E-03`,
 			want: []R{
-				{T: json.Number, V: float32(-1.02e-3)},
-				{T: json.EOF},
+				{V: F32{-1.02e-3}},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds max float32 limit, but should be ok for float64.
-			input: `3.4e39`,
+			in: `3.4e39`,
 			want: []R{
-				{T: json.Number, V: float64(3.4e39)},
-				{T: json.EOF},
+				{V: F64{3.4e39}},
+				{V: EOF},
 			},
 		},
+
 		{
 			// Exceeds max float32 limit.
-			input: `3.4e39`,
+			in: `3.4e39`,
 			want: []R{
-				{T: json.Number, V: float32(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotF32},
+				{V: EOF},
 			},
 		},
 		{
 			// Less than negative max float32 limit.
-			input: `-3.4e39`,
+			in: `-3.4e39`,
 			want: []R{
-				{T: json.Number, V: float32(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotF32},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds max float64 limit.
-			input: `1.79e+309`,
+			in: `1.79e+309`,
 			want: []R{
-				{T: json.Number, V: float64(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotF64},
+				{V: EOF},
 			},
 		},
 		{
 			// Less than negative max float64 limit.
-			input: `-1.79e+309`,
+			in: `-1.79e+309`,
 			want: []R{
-				{T: json.Number, V: float64(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotF64},
+				{V: EOF},
 			},
 		},
 
 		// JSON numbers as signed integers.
 		{
-			input: space + `0` + space,
+			in: space + `0` + space,
 			want: []R{
-				{T: json.Number, V: int32(0)},
-				{T: json.EOF},
+				{V: I32{0}},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `-0` + space,
+			in: space + `-0` + space,
 			want: []R{
-				{T: json.Number, V: int32(0)},
-				{T: json.EOF},
+				{V: I32{0}},
+				{V: EOF},
 			},
 		},
 		{
 			// Fractional part equals 0 is ok.
-			input: `1.00000`,
+			in: `1.00000`,
 			want: []R{
-				{T: json.Number, V: int32(1)},
-				{T: json.EOF},
+				{V: I32{1}},
+				{V: EOF},
 			},
 		},
 		{
 			// Fractional part not equals 0 returns error.
-			input: `1.0000000001`,
+			in: `1.0000000001`,
 			want: []R{
-				{T: json.Number, V: int32(0), VE: `cannot convert 1.0000000001 to integer`},
-				{T: json.EOF},
+				{V: NotI32},
+				{V: EOF},
 			},
 		},
 		{
-			input: `0e0`,
+			in: `0e0`,
 			want: []R{
-				{T: json.Number, V: int32(0)},
-				{T: json.EOF},
+				{V: I32{0}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `0.0E0`,
+			in: `0.0E0`,
 			want: []R{
-				{T: json.Number, V: int32(0)},
-				{T: json.EOF},
+				{V: I32{0}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `0.0E10`,
+			in: `0.0E10`,
 			want: []R{
-				{T: json.Number, V: int32(0)},
-				{T: json.EOF},
+				{V: I32{0}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1`,
+			in: `-1`,
 			want: []R{
-				{T: json.Number, V: int32(-1)},
-				{T: json.EOF},
+				{V: I32{-1}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1.0e+0`,
+			in: `1.0e+0`,
 			want: []R{
-				{T: json.Number, V: int32(1)},
-				{T: json.EOF},
+				{V: I32{1}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1E-0`,
+			in: `-1E-0`,
 			want: []R{
-				{T: json.Number, V: int32(-1)},
-				{T: json.EOF},
+				{V: I32{-1}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `1E1`,
+			in: `1E1`,
 			want: []R{
-				{T: json.Number, V: int32(10)},
-				{T: json.EOF},
+				{V: I32{10}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-100.00e-02`,
+			in: `-100.00e-02`,
 			want: []R{
-				{T: json.Number, V: int32(-1)},
-				{T: json.EOF},
+				{V: I32{-1}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `0.1200E+02`,
+			in: `0.1200E+02`,
 			want: []R{
-				{T: json.Number, V: int64(12)},
-				{T: json.EOF},
+				{V: I64{12}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `0.012e2`,
+			in: `0.012e2`,
 			want: []R{
-				{T: json.Number, V: int32(0), VE: `cannot convert 0.012e2 to integer`},
-				{T: json.EOF},
+				{V: NotI32},
+				{V: EOF},
 			},
 		},
 		{
-			input: `12e-2`,
+			in: `12e-2`,
 			want: []R{
-				{T: json.Number, V: int32(0), VE: `cannot convert 12e-2 to integer`},
-				{T: json.EOF},
+				{V: NotI32},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MaxInt32.
-			input: `2147483648`,
+			in: `2147483648`,
 			want: []R{
-				{T: json.Number, V: int32(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotI32},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MinInt32.
-			input: `-2147483649`,
+			in: `-2147483649`,
 			want: []R{
-				{T: json.Number, V: int32(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotI32},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MaxInt32, but ok for int64.
-			input: `2147483648`,
+			in: `2147483648`,
 			want: []R{
-				{T: json.Number, V: int64(2147483648)},
-				{T: json.EOF},
+				{V: I64{2147483648}},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MinInt32, but ok for int64.
-			input: `-2147483649`,
+			in: `-2147483649`,
 			want: []R{
-				{T: json.Number, V: int64(-2147483649)},
-				{T: json.EOF},
+				{V: I64{-2147483649}},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MaxInt64.
-			input: `9223372036854775808`,
+			in: `9223372036854775808`,
 			want: []R{
-				{T: json.Number, V: int64(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotI64},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MinInt64.
-			input: `-9223372036854775809`,
+			in: `-9223372036854775809`,
 			want: []R{
-				{T: json.Number, V: int64(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotI64},
+				{V: EOF},
 			},
 		},
 
 		// JSON numbers as unsigned integers.
 		{
-			input: space + `0` + space,
+			in: space + `0` + space,
 			want: []R{
-				{T: json.Number, V: uint32(0)},
-				{T: json.EOF},
+				{V: Ui32{0}},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `-0` + space,
+			in: space + `-0` + space,
 			want: []R{
-				{T: json.Number, V: uint32(0)},
-				{T: json.EOF},
+				{V: Ui32{0}},
+				{V: EOF},
 			},
 		},
 		{
-			input: `-1`,
+			in: `-1`,
 			want: []R{
-				{T: json.Number, V: uint32(0), VE: `invalid syntax`},
-				{T: json.EOF},
+				{V: NotUi32},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MaxUint32.
-			input: `4294967296`,
+			in: `4294967296`,
 			want: []R{
-				{T: json.Number, V: uint32(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotUi32},
+				{V: EOF},
 			},
 		},
 		{
 			// Exceeds math.MaxUint64.
-			input: `18446744073709551616`,
+			in: `18446744073709551616`,
 			want: []R{
-				{T: json.Number, V: uint64(0), VE: `value out of range`},
-				{T: json.EOF},
+				{V: NotUi64},
+				{V: EOF},
 			},
 		},
 
 		// JSON sequence of values.
 		{
-			input: `true null`,
+			in: `true null`,
 			want: []R{
-				{T: json.Bool, V: true},
-				{E: `unexpected value null`},
+				{V: Bool{true}},
+				{E: `(line 1:6): unexpected token null`},
 			},
 		},
 		{
-			input: "null false",
+			in: "null false",
 			want: []R{
-				{T: json.Null},
-				{E: `unexpected value false`},
+				{V: Null},
+				{E: `unexpected token false`},
 			},
 		},
 		{
-			input: `true,false`,
+			in: `true,false`,
 			want: []R{
-				{T: json.Bool, V: true},
-				{E: `unexpected character ,`},
+				{V: Bool{true}},
+				{E: `unexpected token ,`},
 			},
 		},
 		{
-			input: `47"hello"`,
+			in: `47"hello"`,
 			want: []R{
-				{T: json.Number, V: int32(47)},
-				{E: `unexpected value "hello"`},
+				{V: I32{47}},
+				{E: `unexpected token "hello"`},
 			},
 		},
 		{
-			input: `47 "hello"`,
+			in: `47 "hello"`,
 			want: []R{
-				{T: json.Number, V: int32(47)},
-				{E: `unexpected value "hello"`},
+				{V: I32{47}},
+				{E: `unexpected token "hello"`},
 			},
 		},
 		{
-			input: `true 42`,
+			in: `true 42`,
 			want: []R{
-				{T: json.Bool, V: true},
-				{E: `unexpected value 42`},
+				{V: Bool{true}},
+				{E: `unexpected token 42`},
 			},
 		},
 
 		// JSON arrays.
 		{
-			input: space + `[]` + space,
+			in: space + `[]` + space,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.EndArray},
-				{T: json.EOF},
+				{V: ArrayOpen},
+				{V: ArrayClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `[` + space + `]` + space,
+			in: space + `[` + space + `]` + space,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.EndArray},
-				{T: json.EOF},
+				{V: ArrayOpen, P: len(space), RS: `[`},
+				{V: ArrayClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `[` + space,
+			in: space + `[` + space,
 			want: []R{
-				{T: json.StartArray},
-				{E: `unexpected EOF`},
+				{V: ArrayOpen},
+				{E: errEOF},
 			},
 		},
 		{
-			input: space + `]` + space,
-			want:  []R{{E: `unexpected character ]`}},
+			in:   space + `]` + space,
+			want: []R{{E: `unexpected token ]`}},
 		},
 		{
-			input: `[null,true,false,  1e1, "hello"   ]`,
+			in: `[null,true,false,  1e1, "hello"   ]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.Null},
-				{T: json.Bool, V: true},
-				{T: json.Bool, V: false},
-				{T: json.Number, V: int32(10)},
-				{T: json.String, V: "hello"},
-				{T: json.EndArray},
-				{T: json.EOF},
+				{V: ArrayOpen},
+				{V: Null},
+				{V: Bool{true}},
+				{V: Bool{false}},
+				{V: I32{10}},
+				{V: Str{"hello"}},
+				{V: ArrayClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: `[` + space + `true` + space + `,` + space + `"hello"` + space + `]`,
+			in: `[` + space + `true` + space + `,` + space + `"hello"` + space + `]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.Bool, V: true},
-				{T: json.String, V: "hello"},
-				{T: json.EndArray},
-				{T: json.EOF},
+				{V: ArrayOpen},
+				{V: Bool{true}},
+				{V: Str{"hello"}},
+				{V: ArrayClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: `[` + space + `true` + space + `,` + space + `]`,
+			in: `[` + space + `true` + space + `,` + space + `]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.Bool, V: true},
-				{E: `unexpected character ]`},
+				{V: ArrayOpen},
+				{V: Bool{true}},
+				{E: `unexpected token ]`},
 			},
 		},
 		{
-			input: `[` + space + `false` + space + `]`,
+			in: `[` + space + `false` + space + `]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.Bool, V: false},
-				{T: json.EndArray},
-				{T: json.EOF},
+				{V: ArrayOpen},
+				{V: Bool{false}},
+				{V: ArrayClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: `[` + space + `1` + space + `0` + space + `]`,
+			in: `[` + space + `1` + space + `0` + space + `]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.Number, V: int64(1)},
-				{E: `unexpected value 0`},
+				{V: ArrayOpen},
+				{V: I64{1}},
+				{E: `unexpected token 0`},
 			},
 		},
 		{
-			input: `[null`,
+			in: `[null`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.Null},
-				{E: `unexpected EOF`},
+				{V: ArrayOpen},
+				{V: Null},
+				{E: errEOF},
 			},
 		},
 		{
-			input: `[foo]`,
+			in: `[foo]`,
 			want: []R{
-				{T: json.StartArray},
+				{V: ArrayOpen},
 				{E: `invalid value foo`},
 			},
 		},
 		{
-			input: `[{}, "hello", [true, false], null]`,
+			in: `[{}, "hello", [true, false], null]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.StartObject},
-				{T: json.EndObject},
-				{T: json.String, V: "hello"},
-				{T: json.StartArray},
-				{T: json.Bool, V: true},
-				{T: json.Bool, V: false},
-				{T: json.EndArray},
-				{T: json.Null},
-				{T: json.EndArray},
-				{T: json.EOF},
+				{V: ArrayOpen},
+				{V: ObjectOpen},
+				{V: ObjectClose},
+				{V: Str{"hello"}},
+				{V: ArrayOpen},
+				{V: Bool{true}},
+				{V: Bool{false}},
+				{V: ArrayClose},
+				{V: Null},
+				{V: ArrayClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: `[{ ]`,
+			in: `[{ ]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.StartObject},
-				{E: `unexpected character ]`},
+				{V: ArrayOpen},
+				{V: ObjectOpen},
+				{E: `unexpected token ]`},
 			},
 		},
 		{
-			input: `[[ ]`,
+			in: `[[ ]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.StartArray},
-				{T: json.EndArray},
-				{E: `unexpected EOF`},
+				{V: ArrayOpen},
+				{V: ArrayOpen},
+				{V: ArrayClose},
+				{E: errEOF},
 			},
 		},
 		{
-			input: `[,]`,
+			in: `[,]`,
 			want: []R{
-				{T: json.StartArray},
-				{E: `unexpected character ,`},
+				{V: ArrayOpen},
+				{E: `unexpected token ,`},
 			},
 		},
 		{
-			input: `[true "hello"]`,
+			in: `[true "hello"]`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.Bool, V: true},
-				{E: `unexpected value "hello"`},
+				{V: ArrayOpen},
+				{V: Bool{true}},
+				{E: `unexpected token "hello"`},
 			},
 		},
 		{
-			input: `[] null`,
+			in: `[] null`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.EndArray},
-				{E: `unexpected value null`},
+				{V: ArrayOpen},
+				{V: ArrayClose},
+				{E: `unexpected token null`},
 			},
 		},
 		{
-			input: `true []`,
+			in: `true []`,
 			want: []R{
-				{T: json.Bool, V: true},
-				{E: `unexpected character [`},
+				{V: Bool{true}},
+				{E: `unexpected token [`},
 			},
 		},
 
 		// JSON objects.
 		{
-			input: space + `{}` + space,
+			in: space + `{}` + space,
 			want: []R{
-				{T: json.StartObject},
-				{T: json.EndObject},
-				{T: json.EOF},
+				{V: ObjectOpen},
+				{V: ObjectClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `{` + space + `}` + space,
+			in: space + `{` + space + `}` + space,
 			want: []R{
-				{T: json.StartObject},
-				{T: json.EndObject},
-				{T: json.EOF},
+				{V: ObjectOpen},
+				{V: ObjectClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: space + `{` + space,
+			in: space + `{` + space,
 			want: []R{
-				{T: json.StartObject},
-				{E: `unexpected EOF`},
+				{V: ObjectOpen},
+				{E: errEOF},
 			},
 		},
 		{
-			input: space + `}` + space,
-			want:  []R{{E: `unexpected character }`}},
+			in:   space + `}` + space,
+			want: []R{{E: `unexpected token }`}},
 		},
 		{
-			input: `{` + space + `null` + space + `}`,
+			in: `{` + space + `null` + space + `}`,
 			want: []R{
-				{T: json.StartObject},
-				{E: `unexpected value null`},
+				{V: ObjectOpen},
+				{E: `unexpected token null`},
 			},
 		},
 		{
-			input: `{[]}`,
+			in: `{[]}`,
 			want: []R{
-				{T: json.StartObject},
-				{E: `unexpected character [`},
+				{V: ObjectOpen},
+				{E: `(line 1:2): unexpected token [`},
 			},
 		},
 		{
-			input: `{,}`,
+			in: `{,}`,
 			want: []R{
-				{T: json.StartObject},
-				{E: `unexpected character ,`},
+				{V: ObjectOpen},
+				{E: `unexpected token ,`},
 			},
 		},
 		{
-			input: `{"345678"}`,
+			in: `{"345678"}`,
 			want: []R{
-				{T: json.StartObject},
-				{E: `unexpected character }, missing ":" after object name`},
+				{V: ObjectOpen},
+				{E: `(line 1:10): unexpected character }, missing ":" after field name`},
 			},
 		},
 		{
-			input: `{` + space + `"hello"` + space + `:` + space + `"world"` + space + `}`,
+			in: `{` + space + `"hello"` + space + `:` + space + `"world"` + space + `}`,
 			want: []R{
-				{T: json.StartObject},
-				{T: json.Name, V: "hello"},
-				{T: json.String, V: "world"},
-				{T: json.EndObject},
-				{T: json.EOF},
+				{V: ObjectOpen},
+				{V: Name{"hello"}, P: len(space) + 1, RS: `"hello"`},
+				{V: Str{"world"}, RS: `"world"`},
+				{V: ObjectClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: `{"hello" "world"}`,
+			in: `{"hello" "world"}`,
 			want: []R{
-				{T: json.StartObject},
-				{E: `unexpected character ", missing ":" after object name`},
+				{V: ObjectOpen},
+				{E: `(line 1:10): unexpected character ", missing ":" after field name`},
 			},
 		},
 		{
-			input: `{"hello":`,
+			in: `{"hello":`,
 			want: []R{
-				{T: json.StartObject},
-				{T: json.Name, V: "hello"},
-				{E: `unexpected EOF`},
+				{V: ObjectOpen},
+				{V: Name{"hello"}},
+				{E: errEOF},
 			},
 		},
 		{
-			input: `{"hello":"world"`,
+			in: `{"hello":"world"`,
 			want: []R{
-				{T: json.StartObject},
-				{T: json.Name, V: "hello"},
-				{T: json.String, V: "world"},
-				{E: `unexpected EOF`},
+				{V: ObjectOpen},
+				{V: Name{"hello"}},
+				{V: Str{"world"}},
+				{E: errEOF},
 			},
 		},
 		{
-			input: `{"hello":"world",`,
+			in: `{"hello":"world",`,
 			want: []R{
-				{T: json.StartObject},
-				{T: json.Name, V: "hello"},
-				{T: json.String, V: "world"},
-				{E: `unexpected EOF`},
+				{V: ObjectOpen},
+				{V: Name{"hello"}},
+				{V: Str{"world"}},
+				{E: errEOF},
 			},
 		},
 		{
-			input: `{""`,
+			in: `{""`,
 			want: []R{
-				{T: json.StartObject},
-				{E: `syntax error (line 1:4): unexpected EOF`},
+				{V: ObjectOpen},
+				{E: errEOF},
 			},
 		},
 		{
-			input: `{"34":"89",}`,
+			in: `{"34":"89",}`,
 			want: []R{
-				{T: json.StartObject},
-				{T: json.Name, V: "34"},
-				{T: json.String, V: "89"},
-				{E: `syntax error (line 1:12): unexpected character }`},
+				{V: ObjectOpen},
+				{V: Name{"34"}, RS: `"34"`},
+				{V: Str{"89"}},
+				{E: `syntax error (line 1:12): unexpected token }`},
 			},
 		},
 		{
-			input: `{
-  "number": 123e2,
-  "bool"  : false,
-  "object": {"string": "world"},
-  "null"  : null,
-  "array" : [1.01, "hello", true],
-  "string": "hello"
-}`,
+			in: `{
+			  "number": 123e2,
+			  "bool"  : false,
+			  "object": {"string": "world"},
+			  "null"  : null,
+			  "array" : [1.01, "hello", true],
+			  "string": "hello"
+			}`,
 			want: []R{
-				{T: json.StartObject},
+				{V: ObjectOpen},
 
-				{T: json.Name, V: "number"},
-				{T: json.Number, V: int32(12300)},
+				{V: Name{"number"}},
+				{V: I32{12300}},
 
-				{T: json.Name, V: "bool"},
-				{T: json.Bool, V: false},
+				{V: Name{"bool"}},
+				{V: Bool{false}},
 
-				{T: json.Name, V: "object"},
-				{T: json.StartObject},
-				{T: json.Name, V: "string"},
-				{T: json.String, V: "world"},
-				{T: json.EndObject},
+				{V: Name{"object"}},
+				{V: ObjectOpen},
+				{V: Name{"string"}},
+				{V: Str{"world"}},
+				{V: ObjectClose},
 
-				{T: json.Name, V: "null"},
-				{T: json.Null},
+				{V: Name{"null"}},
+				{V: Null},
 
-				{T: json.Name, V: "array"},
-				{T: json.StartArray},
-				{T: json.Number, V: float32(1.01)},
-				{T: json.String, V: "hello"},
-				{T: json.Bool, V: true},
-				{T: json.EndArray},
+				{V: Name{"array"}},
+				{V: ArrayOpen},
+				{V: F32{1.01}},
+				{V: Str{"hello"}},
+				{V: Bool{true}},
+				{V: ArrayClose},
 
-				{T: json.Name, V: "string"},
-				{T: json.String, V: "hello"},
+				{V: Name{"string"}},
+				{V: Str{"hello"}},
 
-				{T: json.EndObject},
-				{T: json.EOF},
+				{V: ObjectClose},
+				{V: EOF},
 			},
 		},
 		{
-			input: `[
-  {"object": {"number": 47}},
-  ["list"],
-  null
-]`,
+			in: `[
+			  {"object": {"number": 47}},
+			  ["list"],
+			  null
+			]`,
 			want: []R{
-				{T: json.StartArray},
+				{V: ArrayOpen},
 
-				{T: json.StartObject},
-				{T: json.Name, V: "object"},
-				{T: json.StartObject},
-				{T: json.Name, V: "number"},
-				{T: json.Number, V: uint32(47)},
-				{T: json.EndObject},
-				{T: json.EndObject},
+				{V: ObjectOpen},
+				{V: Name{"object"}},
+				{V: ObjectOpen},
+				{V: Name{"number"}},
+				{V: I32{47}},
+				{V: ObjectClose},
+				{V: ObjectClose},
 
-				{T: json.StartArray},
-				{T: json.String, V: "list"},
-				{T: json.EndArray},
+				{V: ArrayOpen},
+				{V: Str{"list"}},
+				{V: ArrayClose},
 
-				{T: json.Null},
+				{V: Null},
 
-				{T: json.EndArray},
-				{T: json.EOF},
+				{V: ArrayClose},
+				{V: EOF},
 			},
 		},
 
 		// Tests for line and column info.
 		{
-			input: `12345678 x`,
+			in: `12345678 x`,
 			want: []R{
-				{T: json.Number, V: int64(12345678)},
+				{V: I64{12345678}},
 				{E: `syntax error (line 1:10): invalid value x`},
 			},
 		},
 		{
-			input: "\ntrue\n   x",
+			in: "\ntrue\n   x",
 			want: []R{
-				{T: json.Bool, V: true},
+				{V: Bool{true}},
 				{E: `syntax error (line 3:4): invalid value x`},
 			},
 		},
 		{
-			input: `"💩"x`,
+			in: `"💩"x`,
 			want: []R{
-				{T: json.String, V: "💩"},
+				{V: Str{"💩"}},
 				{E: `syntax error (line 1:4): invalid value x`},
 			},
 		},
 		{
-			input: "\n\n[\"🔥🔥🔥\"x",
+			in: "\n\n[\"🔥🔥🔥\"x",
 			want: []R{
-				{T: json.StartArray},
-				{T: json.String, V: "🔥🔥🔥"},
+				{V: ArrayOpen},
+				{V: Str{"🔥🔥🔥"}},
 				{E: `syntax error (line 3:7): invalid value x`},
 			},
 		},
 		{
 			// Multi-rune emojis.
-			input: `["👍🏻👍🏿"x`,
+			in: `["👍🏻👍🏿"x`,
 			want: []R{
-				{T: json.StartArray},
-				{T: json.String, V: "👍🏻👍🏿"},
+				{V: ArrayOpen},
+				{V: Str{"👍🏻👍🏿"}},
 				{E: `syntax error (line 1:8): invalid value x`},
 			},
 		},
-		{
-			input: `{
-  "45678":-1
-}`,
-			want: []R{
-				{T: json.StartObject},
-				{T: json.Name, V: "45678"},
-				{T: json.Number, V: uint64(1), VE: "error (line 2:11)"},
-			},
-		},
 	}
 
 	for _, tc := range tests {
 		tc := tc
 		t.Run("", func(t *testing.T) {
-			dec := json.NewDecoder([]byte(tc.input))
+			dec := json.NewDecoder([]byte(tc.in))
 			for i, want := range tc.want {
-				typ := dec.Peek()
-				if typ != want.T {
-					t.Errorf("input: %v\nPeek() got %v want %v", tc.input, typ, want.T)
-				}
-				value, err := dec.Read()
+				peekTok, peekErr := dec.Peek()
+				tok, err := dec.Read()
 				if err != nil {
 					if want.E == "" {
-						t.Errorf("input: %v\nRead() got unexpected error: %v", tc.input, err)
-
+						errorf(t, tc.in, "want#%d: Read() got unexpected error: %v", i, err)
 					} else if !strings.Contains(err.Error(), want.E) {
-						t.Errorf("input: %v\nRead() got %q, want %q", tc.input, err, want.E)
+						errorf(t, tc.in, "want#%d: Read() got %q, want %q", i, err, want.E)
 					}
-				} else {
-					if want.E != "" {
-						t.Errorf("input: %v\nRead() got nil error, want %q", tc.input, want.E)
-					}
+					return
 				}
-				token := value.Type()
-				if token != want.T {
-					t.Errorf("input: %v\nRead() got %v, want %v", tc.input, token, want.T)
-					break
+				if want.E != "" {
+					errorf(t, tc.in, "want#%d: Read() got nil error, want %q", i, want.E)
+					return
 				}
-				checkValue(t, value, i, want)
+				checkToken(t, tok, i, want, tc.in)
+				if !cmp.Equal(tok, peekTok, cmp.Comparer(json.TokenEquals)) {
+					errorf(t, tc.in, "want#%d: Peek() %+v != Read() token %+v", i, peekTok, tok)
+				}
+				if err != peekErr {
+					errorf(t, tc.in, "want#%d: Peek() error %v != Read() error %v", i, err, peekErr)
+				}
 			}
 		})
 	}
 }
 
-func checkValue(t *testing.T, value json.Value, wantIdx int, want R) {
-	var got interface{}
-	var err error
-	switch value.Type() {
-	case json.Bool:
-		got, err = value.Bool()
-	case json.String:
-		got = value.String()
-	case json.Name:
-		got, err = value.Name()
-	case json.Number:
-		switch want.V.(type) {
-		case float32:
-			got, err = value.Float(32)
-			got = float32(got.(float64))
-		case float64:
-			got, err = value.Float(64)
-		case int32:
-			got, err = value.Int(32)
-			got = int32(got.(int64))
-		case int64:
-			got, err = value.Int(64)
-		case uint32:
-			got, err = value.Uint(32)
-			got = uint32(got.(uint64))
-		case uint64:
-			got, err = value.Uint(64)
+func checkToken(t *testing.T, tok json.Token, idx int, r R, in string) {
+	// Validate Token.Pos() if R.P is set.
+	if r.P > 0 {
+		got := tok.Pos()
+		if got != r.P {
+			errorf(t, in, "want#%d: Token.Pos() got %v want %v", idx, got, r.P)
 		}
-	default:
-		return
 	}
-
-	if err != nil {
-		if want.VE == "" {
-			t.Errorf("want%d: %v got unexpected error: %v", wantIdx, value, err)
-		} else if !strings.Contains(err.Error(), want.VE) {
-			t.Errorf("want#%d: %v got %q, want %q", wantIdx, value, err, want.VE)
-		}
-		return
-	} else {
-		if want.VE != "" {
-			t.Errorf("want#%d: %v got nil error, want %q", wantIdx, value, want.VE)
-			return
+	// Validate Token.RawString if R.RS is set.
+	if len(r.RS) > 0 {
+		got := tok.RawString()
+		if got != r.RS {
+			errorf(t, in, "want#%d: Token.RawString() got %v want %v", idx, got, r.P)
 		}
 	}
 
-	if got != want.V {
-		t.Errorf("want#%d: %v got %v, want %v", wantIdx, value, got, want.V)
+	// Skip checking for Token details if r.V is not set.
+	if r.V == nil {
+		return
 	}
+
+	if err := r.V.check(tok); err != "" {
+		errorf(t, in, "want#%d: %s", idx, err)
+	}
+	return
+}
+
+func errorf(t *testing.T, in string, fmtStr string, args ...interface{}) {
+	t.Helper()
+	vargs := []interface{}{in}
+	for _, arg := range args {
+		vargs = append(vargs, arg)
+	}
+	t.Errorf("input:\n%s\n~end~\n"+fmtStr, vargs...)
 }
 
 func TestClone(t *testing.T) {
@@ -1123,7 +1387,7 @@
 	compareDecoders(t, dec, clone)
 
 	// Advance to inner object, clone and compare again.
-	dec.Read() // Read StartObject.
+	dec.Read() // Read ObjectOpen.
 	dec.Read() // Read Name.
 	clone = dec.Clone()
 	compareDecoders(t, dec, clone)
@@ -1131,18 +1395,18 @@
 
 func compareDecoders(t *testing.T, d1 *json.Decoder, d2 *json.Decoder) {
 	for {
-		v1, err1 := d1.Read()
-		v2, err2 := d2.Read()
-		if v1.Type() != v2.Type() {
-			t.Errorf("cloned decoder: got Type %v, want %v", v2.Type(), v1.Type())
+		tok1, err1 := d1.Read()
+		tok2, err2 := d2.Read()
+		if tok1.Kind() != tok2.Kind() {
+			t.Errorf("cloned decoder: got Kind %v, want %v", tok2.Kind(), tok1.Kind())
 		}
-		if v1.Raw() != v2.Raw() {
-			t.Errorf("cloned decoder: got Raw %v, want %v", v2.Raw(), v1.Raw())
+		if tok1.RawString() != tok2.RawString() {
+			t.Errorf("cloned decoder: got RawString %v, want %v", tok2.RawString(), tok1.RawString())
 		}
 		if err1 != err2 {
 			t.Errorf("cloned decoder: got error %v, want %v", err2, err1)
 		}
-		if v1.Type() == json.EOF {
+		if tok1.Kind() == json.EOF {
 			break
 		}
 	}
diff --git a/internal/encoding/json/decode_token.go b/internal/encoding/json/decode_token.go
new file mode 100644
index 0000000..2eb7023
--- /dev/null
+++ b/internal/encoding/json/decode_token.go
@@ -0,0 +1,193 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package json
+
+import (
+	"bytes"
+	"fmt"
+	"strconv"
+)
+
+// Kind represents a token kind expressible in the JSON format.
+type Kind uint16
+
+const (
+	Invalid Kind = (1 << iota) / 2
+	EOF
+	Null
+	Bool
+	Number
+	String
+	Name
+	ObjectOpen
+	ObjectClose
+	ArrayOpen
+	ArrayClose
+
+	// comma is only for parsing in between tokens and
+	// does not need to be exported.
+	comma
+)
+
+func (k Kind) String() string {
+	switch k {
+	case EOF:
+		return "eof"
+	case Null:
+		return "null"
+	case Bool:
+		return "bool"
+	case Number:
+		return "number"
+	case String:
+		return "string"
+	case ObjectOpen:
+		return "{"
+	case ObjectClose:
+		return "}"
+	case Name:
+		return "name"
+	case ArrayOpen:
+		return "["
+	case ArrayClose:
+		return "]"
+	case comma:
+		return ","
+	}
+	return "<invalid>"
+}
+
+// Token provides a parsed token kind and value.
+//
+// Values are provided by the difference accessor methods. The accessor methods
+// Name, Bool, and ParsedString will panic if called on the wrong kind. There
+// are different accessor methods for the Number kind for converting to the
+// appropriate Go numeric type and those methods have the ok return value.
+type Token struct {
+	// Token kind.
+	kind Kind
+	// pos provides the position of the token in the original input.
+	pos int
+	// raw bytes of the serialized token.
+	// This is a subslice into the original input.
+	raw []byte
+	// boo is parsed boolean value.
+	boo bool
+	// str is parsed string value.
+	str string
+}
+
+// Kind returns the token kind.
+func (t Token) Kind() Kind {
+	return t.kind
+}
+
+// RawString returns the read value in string.
+func (t Token) RawString() string {
+	return string(t.raw)
+}
+
+// Pos returns the token position from the input.
+func (t Token) Pos() int {
+	return t.pos
+}
+
+// Name returns the object name if token is Name, else it will return an error.
+func (t Token) Name() string {
+	if t.kind == Name {
+		return t.str
+	}
+	panic(fmt.Sprintf("Token is not a Name: %v", t.RawString()))
+}
+
+// Bool returns the bool value if token kind is Bool, else it panics.
+func (t Token) Bool() bool {
+	if t.kind == Bool {
+		return t.boo
+	}
+	panic(fmt.Sprintf("Token is not a Bool: %v", t.RawString()))
+}
+
+// ParsedString returns the string value for a JSON string token or the read
+// value in string if token is not a string.
+func (t Token) ParsedString() string {
+	if t.kind == String {
+		return t.str
+	}
+	panic(fmt.Sprintf("Token is not a String: %v", t.RawString()))
+}
+
+// Float returns the floating-point number if token kind is Number.
+//
+// The floating-point precision is specified by the bitSize parameter: 32 for
+// float32 or 64 for float64. If bitSize=32, the result still has type float64,
+// but it will be convertible to float32 without changing its value. It will
+// return false if the number exceeds the floating point limits for given
+// bitSize.
+func (t Token) Float(bitSize int) (float64, bool) {
+	if t.kind != Number {
+		return 0, false
+	}
+	f, err := strconv.ParseFloat(t.RawString(), bitSize)
+	if err != nil {
+		return 0, false
+	}
+	return f, true
+}
+
+// Int returns the signed integer number if token is Number.
+//
+// The given bitSize specifies the integer type that the result must fit into.
+// It returns false if the number is not an integer value or if the result
+// exceeds the limits for given bitSize.
+func (t Token) Int(bitSize int) (int64, bool) {
+	s, ok := t.getIntStr()
+	if !ok {
+		return 0, false
+	}
+	n, err := strconv.ParseInt(s, 10, bitSize)
+	if err != nil {
+		return 0, false
+	}
+	return n, true
+}
+
+// Uint returns the signed integer number if token is Number, else it will
+// return an error.
+//
+// The given bitSize specifies the unsigned integer type that the result must
+// fit into. It returns false if the number is not an unsigned integer value
+// or if the result exceeds the limits for given bitSize.
+func (t Token) Uint(bitSize int) (uint64, bool) {
+	s, ok := t.getIntStr()
+	if !ok {
+		return 0, false
+	}
+	n, err := strconv.ParseUint(s, 10, bitSize)
+	if err != nil {
+		return 0, false
+	}
+	return n, true
+}
+
+func (t Token) getIntStr() (string, bool) {
+	if t.kind != Number {
+		return "", false
+	}
+	parts, ok := parseNumberParts(t.raw)
+	if !ok {
+		return "", false
+	}
+	return normalizeToIntString(parts)
+}
+
+// TokenEquals returns true if given Tokens are equal, else false.
+func TokenEquals(x, y Token) bool {
+	return x.kind == y.kind &&
+		x.pos == y.pos &&
+		bytes.Equal(x.raw, y.raw) &&
+		x.boo == y.boo &&
+		x.str == y.str
+}
diff --git a/internal/encoding/json/encode.go b/internal/encoding/json/encode.go
index 741f34f..17dec31 100644
--- a/internal/encoding/json/encode.go
+++ b/internal/encoding/json/encode.go
@@ -5,18 +5,34 @@
 package json
 
 import (
+	"math"
+	"math/bits"
 	"strconv"
 	"strings"
+	"unicode/utf8"
 
 	"google.golang.org/protobuf/internal/detrand"
 	"google.golang.org/protobuf/internal/errors"
 )
 
+// kind represents an encoding type.
+type kind uint8
+
+const (
+	_ kind = (1 << iota) / 2
+	name
+	scalar
+	objectOpen
+	objectClose
+	arrayOpen
+	arrayClose
+)
+
 // Encoder provides methods to write out JSON constructs and values. The user is
 // responsible for producing valid sequences of JSON constructs and values.
 type Encoder struct {
 	indent   string
-	lastType Type
+	lastKind kind
 	indents  []byte
 	out      []byte
 }
@@ -43,13 +59,13 @@
 
 // WriteNull writes out the null value.
 func (e *Encoder) WriteNull() {
-	e.prepareNext(Null)
+	e.prepareNext(scalar)
 	e.out = append(e.out, "null"...)
 }
 
 // WriteBool writes out the given boolean value.
 func (e *Encoder) WriteBool(b bool) {
-	e.prepareNext(Bool)
+	e.prepareNext(scalar)
 	if b {
 		e.out = append(e.out, "true"...)
 	} else {
@@ -57,9 +73,10 @@
 	}
 }
 
-// WriteString writes out the given string in JSON string value.
+// WriteString writes out the given string in JSON string value. Returns error
+// if input string contains invalid UTF-8.
 func (e *Encoder) WriteString(s string) error {
-	e.prepareNext(String)
+	e.prepareNext(scalar)
 	var err error
 	if e.out, err = appendString(e.out, s); err != nil {
 		return err
@@ -67,42 +84,126 @@
 	return nil
 }
 
+// Sentinel error used for indicating invalid UTF-8.
+var invalidUTF8Err = errors.New("invalid UTF-8")
+
+func appendString(out []byte, in string) ([]byte, error) {
+	out = append(out, '"')
+	i := indexNeedEscapeInString(in)
+	in, out = in[i:], append(out, in[:i]...)
+	for len(in) > 0 {
+		switch r, n := utf8.DecodeRuneInString(in); {
+		case r == utf8.RuneError && n == 1:
+			return out, invalidUTF8Err
+		case r < ' ' || r == '"' || r == '\\':
+			out = append(out, '\\')
+			switch r {
+			case '"', '\\':
+				out = append(out, byte(r))
+			case '\b':
+				out = append(out, 'b')
+			case '\f':
+				out = append(out, 'f')
+			case '\n':
+				out = append(out, 'n')
+			case '\r':
+				out = append(out, 'r')
+			case '\t':
+				out = append(out, 't')
+			default:
+				out = append(out, 'u')
+				out = append(out, "0000"[1+(bits.Len32(uint32(r))-1)/4:]...)
+				out = strconv.AppendUint(out, uint64(r), 16)
+			}
+			in = in[n:]
+		default:
+			i := indexNeedEscapeInString(in[n:])
+			in, out = in[n+i:], append(out, in[:n+i]...)
+		}
+	}
+	out = append(out, '"')
+	return out, nil
+}
+
+// indexNeedEscapeInString returns the index of the character that needs
+// escaping. If no characters need escaping, this returns the input length.
+func indexNeedEscapeInString(s string) int {
+	for i, r := range s {
+		if r < ' ' || r == '\\' || r == '"' || r == utf8.RuneError {
+			return i
+		}
+	}
+	return len(s)
+}
+
 // WriteFloat writes out the given float and bitSize in JSON number value.
 func (e *Encoder) WriteFloat(n float64, bitSize int) {
-	e.prepareNext(Number)
+	e.prepareNext(scalar)
 	e.out = appendFloat(e.out, n, bitSize)
 }
 
+// appendFloat formats given float in bitSize, and appends to the given []byte.
+func appendFloat(out []byte, n float64, bitSize int) []byte {
+	switch {
+	case math.IsNaN(n):
+		return append(out, `"NaN"`...)
+	case math.IsInf(n, +1):
+		return append(out, `"Infinity"`...)
+	case math.IsInf(n, -1):
+		return append(out, `"-Infinity"`...)
+	}
+
+	// JSON number formatting logic based on encoding/json.
+	// See floatEncoder.encode for reference.
+	fmt := byte('f')
+	if abs := math.Abs(n); abs != 0 {
+		if bitSize == 64 && (abs < 1e-6 || abs >= 1e21) ||
+			bitSize == 32 && (float32(abs) < 1e-6 || float32(abs) >= 1e21) {
+			fmt = 'e'
+		}
+	}
+	out = strconv.AppendFloat(out, n, fmt, -1, bitSize)
+	if fmt == 'e' {
+		n := len(out)
+		if n >= 4 && out[n-4] == 'e' && out[n-3] == '-' && out[n-2] == '0' {
+			out[n-2] = out[n-1]
+			out = out[:n-1]
+		}
+	}
+	return out
+}
+
 // WriteInt writes out the given signed integer in JSON number value.
 func (e *Encoder) WriteInt(n int64) {
-	e.prepareNext(Number)
+	e.prepareNext(scalar)
 	e.out = append(e.out, strconv.FormatInt(n, 10)...)
 }
 
 // WriteUint writes out the given unsigned integer in JSON number value.
 func (e *Encoder) WriteUint(n uint64) {
-	e.prepareNext(Number)
+	e.prepareNext(scalar)
 	e.out = append(e.out, strconv.FormatUint(n, 10)...)
 }
 
 // StartObject writes out the '{' symbol.
 func (e *Encoder) StartObject() {
-	e.prepareNext(StartObject)
+	e.prepareNext(objectOpen)
 	e.out = append(e.out, '{')
 }
 
 // EndObject writes out the '}' symbol.
 func (e *Encoder) EndObject() {
-	e.prepareNext(EndObject)
+	e.prepareNext(objectClose)
 	e.out = append(e.out, '}')
 }
 
 // WriteName writes out the given string in JSON string value and the name
-// separator ':'.
+// separator ':'. Returns error if input string contains invalid UTF-8, which
+// should not be likely as protobuf field names should be valid.
 func (e *Encoder) WriteName(s string) error {
-	e.prepareNext(Name)
-	// Errors returned by appendString() are non-fatal.
+	e.prepareNext(name)
 	var err error
+	// Append to output regardless of error.
 	e.out, err = appendString(e.out, s)
 	e.out = append(e.out, ':')
 	return err
@@ -110,28 +211,28 @@
 
 // StartArray writes out the '[' symbol.
 func (e *Encoder) StartArray() {
-	e.prepareNext(StartArray)
+	e.prepareNext(arrayOpen)
 	e.out = append(e.out, '[')
 }
 
 // EndArray writes out the ']' symbol.
 func (e *Encoder) EndArray() {
-	e.prepareNext(EndArray)
+	e.prepareNext(arrayClose)
 	e.out = append(e.out, ']')
 }
 
 // prepareNext adds possible comma and indentation for the next value based
-// on last type and indent option. It also updates lastType to next.
-func (e *Encoder) prepareNext(next Type) {
+// on last type and indent option. It also updates lastKind to next.
+func (e *Encoder) prepareNext(next kind) {
 	defer func() {
-		// Set lastType to next.
-		e.lastType = next
+		// Set lastKind to next.
+		e.lastKind = next
 	}()
 
 	if len(e.indent) == 0 {
 		// Need to add comma on the following condition.
-		if e.lastType&(Null|Bool|Number|String|EndObject|EndArray) != 0 &&
-			next&(Name|Null|Bool|Number|String|StartObject|StartArray) != 0 {
+		if e.lastKind&(scalar|objectClose|arrayClose) != 0 &&
+			next&(name|scalar|objectOpen|arrayOpen) != 0 {
 			e.out = append(e.out, ',')
 			// For single-line output, add a random extra space after each
 			// comma to make output unstable.
@@ -143,28 +244,28 @@
 	}
 
 	switch {
-	case e.lastType&(StartObject|StartArray) != 0:
+	case e.lastKind&(objectOpen|arrayOpen) != 0:
 		// If next type is NOT closing, add indent and newline.
-		if next&(EndObject|EndArray) == 0 {
+		if next&(objectClose|arrayClose) == 0 {
 			e.indents = append(e.indents, e.indent...)
 			e.out = append(e.out, '\n')
 			e.out = append(e.out, e.indents...)
 		}
 
-	case e.lastType&(Null|Bool|Number|String|EndObject|EndArray) != 0:
+	case e.lastKind&(scalar|objectClose|arrayClose) != 0:
 		switch {
 		// If next type is either a value or name, add comma and newline.
-		case next&(Name|Null|Bool|Number|String|StartObject|StartArray) != 0:
+		case next&(name|scalar|objectOpen|arrayOpen) != 0:
 			e.out = append(e.out, ',', '\n')
 
 		// If next type is a closing object or array, adjust indentation.
-		case next&(EndObject|EndArray) != 0:
+		case next&(objectClose|arrayClose) != 0:
 			e.indents = e.indents[:len(e.indents)-len(e.indent)]
 			e.out = append(e.out, '\n')
 		}
 		e.out = append(e.out, e.indents...)
 
-	case e.lastType&Name != 0:
+	case e.lastKind&name != 0:
 		e.out = append(e.out, ' ')
 		// For multi-line output, add a random extra space after key: to make
 		// output unstable.
diff --git a/internal/encoding/json/string.go b/internal/encoding/json/string.go
deleted file mode 100644
index 0730ffa..0000000
--- a/internal/encoding/json/string.go
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2018 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package json
-
-import (
-	"io"
-	"math/bits"
-	"strconv"
-	"unicode"
-	"unicode/utf16"
-	"unicode/utf8"
-
-	"google.golang.org/protobuf/internal/errors"
-	"google.golang.org/protobuf/internal/strs"
-)
-
-func appendString(out []byte, in string) ([]byte, error) {
-	out = append(out, '"')
-	i := indexNeedEscapeInString(in)
-	in, out = in[i:], append(out, in[:i]...)
-	for len(in) > 0 {
-		switch r, n := utf8.DecodeRuneInString(in); {
-		case r == utf8.RuneError && n == 1:
-			return out, errors.InvalidUTF8("")
-		case r < ' ' || r == '"' || r == '\\':
-			out = append(out, '\\')
-			switch r {
-			case '"', '\\':
-				out = append(out, byte(r))
-			case '\b':
-				out = append(out, 'b')
-			case '\f':
-				out = append(out, 'f')
-			case '\n':
-				out = append(out, 'n')
-			case '\r':
-				out = append(out, 'r')
-			case '\t':
-				out = append(out, 't')
-			default:
-				out = append(out, 'u')
-				out = append(out, "0000"[1+(bits.Len32(uint32(r))-1)/4:]...)
-				out = strconv.AppendUint(out, uint64(r), 16)
-			}
-			in = in[n:]
-		default:
-			i := indexNeedEscapeInString(in[n:])
-			in, out = in[n+i:], append(out, in[:n+i]...)
-		}
-	}
-	out = append(out, '"')
-	return out, nil
-}
-
-func (d *Decoder) parseString(in []byte) (string, int, error) {
-	in0 := in
-	if len(in) == 0 {
-		return "", 0, io.ErrUnexpectedEOF
-	}
-	if in[0] != '"' {
-		return "", 0, d.newSyntaxError("invalid character %q at start of string", in[0])
-	}
-	in = in[1:]
-	i := indexNeedEscapeInBytes(in)
-	in, out := in[i:], in[:i:i] // set cap to prevent mutations
-	for len(in) > 0 {
-		switch r, n := utf8.DecodeRune(in); {
-		case r == utf8.RuneError && n == 1:
-			return "", 0, d.newSyntaxError("invalid UTF-8 in string")
-		case r < ' ':
-			return "", 0, d.newSyntaxError("invalid character %q in string", r)
-		case r == '"':
-			in = in[1:]
-			n := len(in0) - len(in)
-			return string(out), n, nil
-		case r == '\\':
-			if len(in) < 2 {
-				return "", 0, io.ErrUnexpectedEOF
-			}
-			switch r := in[1]; r {
-			case '"', '\\', '/':
-				in, out = in[2:], append(out, r)
-			case 'b':
-				in, out = in[2:], append(out, '\b')
-			case 'f':
-				in, out = in[2:], append(out, '\f')
-			case 'n':
-				in, out = in[2:], append(out, '\n')
-			case 'r':
-				in, out = in[2:], append(out, '\r')
-			case 't':
-				in, out = in[2:], append(out, '\t')
-			case 'u':
-				if len(in) < 6 {
-					return "", 0, io.ErrUnexpectedEOF
-				}
-				v, err := strconv.ParseUint(string(in[2:6]), 16, 16)
-				if err != nil {
-					return "", 0, d.newSyntaxError("invalid escape code %q in string", in[:6])
-				}
-				in = in[6:]
-
-				r := rune(v)
-				if utf16.IsSurrogate(r) {
-					if len(in) < 6 {
-						return "", 0, io.ErrUnexpectedEOF
-					}
-					v, err := strconv.ParseUint(string(in[2:6]), 16, 16)
-					r = utf16.DecodeRune(r, rune(v))
-					if in[0] != '\\' || in[1] != 'u' ||
-						r == unicode.ReplacementChar || err != nil {
-						return "", 0, d.newSyntaxError("invalid escape code %q in string", in[:6])
-					}
-					in = in[6:]
-				}
-				out = append(out, string(r)...)
-			default:
-				return "", 0, d.newSyntaxError("invalid escape code %q in string", in[:2])
-			}
-		default:
-			i := indexNeedEscapeInBytes(in[n:])
-			in, out = in[n+i:], append(out, in[:n+i]...)
-		}
-	}
-	return "", 0, io.ErrUnexpectedEOF
-}
-
-// indexNeedEscapeInString returns the index of the character that needs
-// escaping. If no characters need escaping, this returns the input length.
-func indexNeedEscapeInString(s string) int {
-	for i, r := range s {
-		if r < ' ' || r == '\\' || r == '"' || r == utf8.RuneError {
-			return i
-		}
-	}
-	return len(s)
-}
-func indexNeedEscapeInBytes(b []byte) int { return indexNeedEscapeInString(strs.UnsafeString(b)) }
diff --git a/internal/encoding/json/types.go b/internal/encoding/json/types.go
deleted file mode 100644
index 35feeb7..0000000
--- a/internal/encoding/json/types.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package json
-
-// Type represents a type expressible in the JSON format.
-type Type uint
-
-const (
-	Invalid Type = (1 << iota) / 2
-	EOF
-	Null
-	Bool
-	Number
-	String
-	StartObject
-	EndObject
-	Name
-	StartArray
-	EndArray
-
-	// comma is only for parsing in between values and should not be exposed.
-	comma
-)
-
-func (t Type) String() string {
-	switch t {
-	case EOF:
-		return "eof"
-	case Null:
-		return "null"
-	case Bool:
-		return "bool"
-	case Number:
-		return "number"
-	case String:
-		return "string"
-	case StartObject:
-		return "{"
-	case EndObject:
-		return "}"
-	case Name:
-		return "name"
-	case StartArray:
-		return "["
-	case EndArray:
-		return "]"
-	}
-	return "<invalid>"
-}