Repository: segmentio/encoding
Pull request #72 (2021-03-20 22:39:40)

Preprocess input before parsing
This PR updates `json.Parse()` to first look at the string to determine whether it's valid ASCII print and whether it contains a backslash. This information is then made available to the parsing routines, allowing them to take fast paths without having to check each of their inputs independently.

```
name           old time/op    new time/op    delta
CodeDecoder-4    6.71ms ± 3%    5.49ms ± 1%  -18.18%  (p=0.008 n=5+5)

name           old speed      new speed      delta
CodeDecoder-4   289MB/s ± 3%   354MB/s ± 1%  +22.19%  (p=0.008 n=5+5)
```

Preprocessing the input is quick thanks to SIMD optimized routines (https://github.com/segmentio/encoding/pull/66 and `bytes.IndexByte`). There are perhaps further improvements to be made by coalescing the passes into one.


Patch:
diff --git a/ascii/valid_amd64.s b/ascii/valid_amd64.s
index 2300fde..ac878a8 100644
--- a/ascii/valid_amd64.s
+++ b/ascii/valid_amd64.s
@@ -13,10 +13,8 @@ loop64:
 	CMPQ      CX, $0x04
 	JL        loop32
 	VMOVUPS   (AX), Y0
-	VMOVUPS   32(AX), Y1
+	VPOR      32(AX), Y0, Y0
 	VPMOVMSKB Y0, BX
-	VPMOVMSKB Y1, BP
-	ORL       BP, BX
 	CMPL      BX, $0x00
 	JNE       done
 	SUBQ      $0x04, CX
diff --git a/ascii/valid_asm.go b/ascii/valid_asm.go
index a9f3b62..c8f4032 100644
--- a/ascii/valid_asm.go
+++ b/ascii/valid_asm.go
@@ -16,12 +16,9 @@ func main() {
 	r := GP64()
 	MOVQ(U64(0), r)
 
-	msk0 := GP32()
-	msk1 := GP32()
-
+	msk := GP32()
 	xmm0 := XMM()
 	ymm0 := YMM()
-	ymm1 := YMM()
 
 	Label("loop64")
 	CMPQ(n, Imm(4))
@@ -31,12 +28,10 @@ func main() {
 	// that the current version of avo hasn't yet released support for the
 	// AVX.512 instruction set.
 	VMOVUPS(Mem{Base: p}, ymm0)
-	VMOVUPS((Mem{Base: p}).Offset(32), ymm1)
+	VPOR((Mem{Base: p}).Offset(32), ymm0, ymm0)
 
-	VPMOVMSKB(ymm0, msk0)
-	VPMOVMSKB(ymm1, msk1)
-	ORL(msk1, msk0)
-	CMPL(msk0, Imm(0))
+	VPMOVMSKB(ymm0, msk)
+	CMPL(msk, Imm(0))
 	JNE(LabelRef("done"))
 
 	SUBQ(Imm(4), n)
@@ -50,8 +45,8 @@ func main() {
 	JL(LabelRef("loop16"))
 
 	VMOVUPS(Mem{Base: p}, ymm0)
-	VPMOVMSKB(ymm0, msk0)
-	CMPL(msk0, Imm(0))
+	VPMOVMSKB(ymm0, msk)
+	CMPL(msk, Imm(0))
 	JNE(LabelRef("done"))
 
 	SUBQ(Imm(2), n)
@@ -63,8 +58,8 @@ func main() {
 	JE(LabelRef("valid"))
 
 	MOVUPS(Mem{Base: p}, xmm0)
-	PMOVMSKB(xmm0, msk0)
-	CMPL(msk0, Imm(0))
+	PMOVMSKB(xmm0, msk)
+	CMPL(msk, Imm(0))
 	JNE(LabelRef("done"))
 
 	Label("valid")
diff --git a/json/codec.go b/json/codec.go
index cb01e8d..5e06a99 100644
--- a/json/codec.go
+++ b/json/codec.go
@@ -983,17 +983,6 @@ func syntaxError(b []byte, msg string, args ...interface{}) error {
 	return e
 }
 
-func inputError(b []byte, t reflect.Type) ([]byte, error) {
-	if len(b) == 0 {
-		return nil, unexpectedEOF(b)
-	}
-	_, r, err := parseValue(b)
-	if err != nil {
-		return r, err
-	}
-	return skipSpaces(r), unmarshalTypeError(b, t)
-}
-
 func objectKeyError(b []byte, err error) ([]byte, error) {
 	if len(b) == 0 {
 		return nil, unexpectedEOF(b)
diff --git a/json/decode.go b/json/decode.go
index 606fd93..01f9201 100644
--- a/json/decode.go
+++ b/json/decode.go
@@ -17,7 +17,7 @@ func (d decoder) decodeNull(b []byte, p unsafe.Pointer) ([]byte, error) {
 	if hasNullPrefix(b) {
 		return b[4:], nil
 	}
-	return inputError(b, nullType)
+	return d.inputError(b, nullType)
 }
 
 func (d decoder) decodeBool(b []byte, p unsafe.Pointer) ([]byte, error) {
@@ -34,7 +34,7 @@ func (d decoder) decodeBool(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 
 	default:
-		return inputError(b, boolType)
+		return d.inputError(b, boolType)
 	}
 }
 
@@ -43,7 +43,7 @@ func (d decoder) decodeInt(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseInt(b, intType)
+	v, r, err := d.parseInt(b, intType)
 	if err != nil {
 		return r, err
 	}
@@ -57,7 +57,7 @@ func (d decoder) decodeInt8(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseInt(b, int8Type)
+	v, r, err := d.parseInt(b, int8Type)
 	if err != nil {
 		return r, err
 	}
@@ -75,7 +75,7 @@ func (d decoder) decodeInt16(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseInt(b, int16Type)
+	v, r, err := d.parseInt(b, int16Type)
 	if err != nil {
 		return r, err
 	}
@@ -93,7 +93,7 @@ func (d decoder) decodeInt32(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseInt(b, int32Type)
+	v, r, err := d.parseInt(b, int32Type)
 	if err != nil {
 		return r, err
 	}
@@ -111,7 +111,7 @@ func (d decoder) decodeInt64(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseInt(b, int64Type)
+	v, r, err := d.parseInt(b, int64Type)
 	if err != nil {
 		return r, err
 	}
@@ -125,7 +125,7 @@ func (d decoder) decodeUint(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseUint(b, uintType)
+	v, r, err := d.parseUint(b, uintType)
 	if err != nil {
 		return r, err
 	}
@@ -139,7 +139,7 @@ func (d decoder) decodeUintptr(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseUint(b, uintptrType)
+	v, r, err := d.parseUint(b, uintptrType)
 	if err != nil {
 		return r, err
 	}
@@ -153,7 +153,7 @@ func (d decoder) decodeUint8(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseUint(b, uint8Type)
+	v, r, err := d.parseUint(b, uint8Type)
 	if err != nil {
 		return r, err
 	}
@@ -171,7 +171,7 @@ func (d decoder) decodeUint16(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseUint(b, uint16Type)
+	v, r, err := d.parseUint(b, uint16Type)
 	if err != nil {
 		return r, err
 	}
@@ -189,7 +189,7 @@ func (d decoder) decodeUint32(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseUint(b, uint32Type)
+	v, r, err := d.parseUint(b, uint32Type)
 	if err != nil {
 		return r, err
 	}
@@ -207,7 +207,7 @@ func (d decoder) decodeUint64(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseUint(b, uint64Type)
+	v, r, err := d.parseUint(b, uint64Type)
 	if err != nil {
 		return r, err
 	}
@@ -221,14 +221,14 @@ func (d decoder) decodeFloat32(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseNumber(b)
+	v, r, err := d.parseNumber(b)
 	if err != nil {
-		return inputError(b, float32Type)
+		return d.inputError(b, float32Type)
 	}
 
 	f, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&v)), 32)
 	if err != nil {
-		return inputError(b, float32Type)
+		return d.inputError(b, float32Type)
 	}
 
 	*(*float32)(p) = float32(f)
@@ -240,14 +240,14 @@ func (d decoder) decodeFloat64(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseNumber(b)
+	v, r, err := d.parseNumber(b)
 	if err != nil {
-		return inputError(b, float64Type)
+		return d.inputError(b, float64Type)
 	}
 
 	f, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&v)), 64)
 	if err != nil {
-		return inputError(b, float64Type)
+		return d.inputError(b, float64Type)
 	}
 
 	*(*float64)(p) = f
@@ -259,9 +259,9 @@ func (d decoder) decodeNumber(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	v, r, err := parseNumber(b)
+	v, r, err := d.parseNumber(b)
 	if err != nil {
-		return inputError(b, numberType)
+		return d.inputError(b, numberType)
 	}
 
 	if (d.flags & DontCopyNumber) != 0 {
@@ -278,10 +278,10 @@ func (d decoder) decodeString(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b[4:], nil
 	}
 
-	s, r, new, err := parseStringUnquote(b, nil)
+	s, r, new, err := d.parseStringUnquote(b, nil)
 	if err != nil {
 		if len(b) == 0 || b[0] != '"' {
-			return inputError(b, stringType)
+			return d.inputError(b, stringType)
 		}
 		return r, err
 	}
@@ -300,9 +300,9 @@ func (d decoder) decodeFromString(b []byte, p unsafe.Pointer, decode decodeFunc)
 		return decode(d, b, p)
 	}
 
-	v, b, _, err := parseStringUnquote(b, nil)
+	v, b, _, err := d.parseStringUnquote(b, nil)
 	if err != nil {
-		return inputError(v, stringType)
+		return d.inputError(v, stringType)
 	}
 
 	if v, err = decode(d, v, p); err != nil {
@@ -322,7 +322,7 @@ func (d decoder) decodeFromStringToInt(b []byte, p unsafe.Pointer, t reflect.Typ
 	}
 
 	if len(b) > 0 && b[0] != '"' {
-		v, r, err := parseNumber(b)
+		v, r, err := d.parseNumber(b)
 		if err == nil {
 			// The encoding/json package will return a *json.UnmarshalTypeError if
 			// the input was a floating point number representation, even tho a
@@ -349,9 +349,9 @@ func (d decoder) decodeFromStringToInt(b []byte, p unsafe.Pointer, t reflect.Typ
 		return b, fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal \"\" into int")
 	}
 
-	v, b, _, err := parseStringUnquote(b, nil)
+	v, b, _, err := d.parseStringUnquote(b, nil)
 	if err != nil {
-		return inputError(v, t)
+		return d.inputError(v, t)
 	}
 
 	if hasLeadingZeroes(v) {
@@ -384,10 +384,10 @@ func (d decoder) decodeFromStringToInt(b []byte, p unsafe.Pointer, t reflect.Typ
 		}
 		// When the input value was a valid number representation we retain the
 		// error returned by the decoder.
-		if _, _, err := parseNumber(v); err != nil {
+		if _, _, err := d.parseNumber(v); err != nil {
 			// When the input value valid JSON we mirror the behavior of the
 			// encoding/json package and return a generic error.
-			if _, _, err := parseValue(v); err == nil {
+			if _, _, err := d.parseValue(v); err == nil {
 				return b, fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into int", prefix(v))
 			}
 		}
@@ -406,7 +406,7 @@ func (d decoder) decodeBytes(b []byte, p unsafe.Pointer) ([]byte, error) {
 	}
 
 	if len(b) < 2 {
-		return inputError(b, bytesType)
+		return d.inputError(b, bytesType)
 	}
 
 	if b[0] != '"' {
@@ -414,14 +414,14 @@ func (d decoder) decodeBytes(b []byte, p unsafe.Pointer) ([]byte, error) {
 		if len(b) > 0 && b[0] == '[' {
 			return d.decodeSlice(b, p, 1, bytesType, decoder.decodeUint8)
 		}
-		return inputError(b, bytesType)
+		return d.inputError(b, bytesType)
 	}
 
 	// The input string contains escaped sequences, we need to parse it before
 	// decoding it to match the encoding/json package behvaior.
-	src, r, _, err := parseStringUnquote(b, nil)
+	src, r, _, err := d.parseStringUnquote(b, nil)
 	if err != nil {
-		return inputError(b, bytesType)
+		return d.inputError(b, bytesType)
 	}
 
 	dst := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
@@ -445,9 +445,9 @@ func (d decoder) decodeDuration(b []byte, p unsafe.Pointer) ([]byte, error) {
 	// flexible on how durations are formatted, but for the time being, it's
 	// been punted to go2 at the earliest: https://github.com/golang/go/issues/4712
 	if len(b) > 0 && b[0] != '"' {
-		v, r, err := parseInt(b, durationType)
+		v, r, err := d.parseInt(b, durationType)
 		if err != nil {
-			return inputError(b, int32Type)
+			return d.inputError(b, int32Type)
 		}
 
 		if v < math.MinInt64 || v > math.MaxInt64 {
@@ -459,19 +459,19 @@ func (d decoder) decodeDuration(b []byte, p unsafe.Pointer) ([]byte, error) {
 	}
 
 	if len(b) < 2 || b[0] != '"' {
-		return inputError(b, durationType)
+		return d.inputError(b, durationType)
 	}
 
 	i := bytes.IndexByte(b[1:], '"') + 1
 	if i <= 0 {
-		return inputError(b, durationType)
+		return d.inputError(b, durationType)
 	}
 
 	s := b[1:i] // trim quotes
 
 	v, err := time.ParseDuration(*(*string)(unsafe.Pointer(&s)))
 	if err != nil {
-		return inputError(b, durationType)
+		return d.inputError(b, durationType)
 	}
 
 	*(*time.Duration)(p) = v
@@ -484,19 +484,19 @@ func (d decoder) decodeTime(b []byte, p unsafe.Pointer) ([]byte, error) {
 	}
 
 	if len(b) < 2 || b[0] != '"' {
-		return inputError(b, timeType)
+		return d.inputError(b, timeType)
 	}
 
 	i := bytes.IndexByte(b[1:], '"') + 1
 	if i <= 0 {
-		return inputError(b, timeType)
+		return d.inputError(b, timeType)
 	}
 
 	s := b[1:i] // trim quotes
 
 	v, err := time.Parse(time.RFC3339Nano, *(*string)(unsafe.Pointer(&s)))
 	if err != nil {
-		return inputError(b, timeType)
+		return d.inputError(b, timeType)
 	}
 
 	*(*time.Time)(p) = v
@@ -509,7 +509,7 @@ func (d decoder) decodeArray(b []byte, p unsafe.Pointer, n int, size uintptr, t
 	}
 
 	if len(b) < 2 || b[0] != '[' {
-		return inputError(b, t)
+		return d.inputError(b, t)
 	}
 	b = b[1:]
 
@@ -557,7 +557,7 @@ func (d decoder) decodeArray(b []byte, p unsafe.Pointer, n int, size uintptr, t
 			return b[1:], nil
 		}
 
-		_, b, err = parseValue(b)
+		_, b, err = d.parseValue(b)
 		if err != nil {
 			return b, err
 		}
@@ -576,7 +576,7 @@ func (d decoder) decodeSlice(b []byte, p unsafe.Pointer, size uintptr, t reflect
 	}
 
 	if len(b) < 2 {
-		return inputError(b, t)
+		return d.inputError(b, t)
 	}
 
 	if b[0] != '[' {
@@ -586,7 +586,7 @@ func (d decoder) decodeSlice(b []byte, p unsafe.Pointer, size uintptr, t reflect
 		if t.Elem().Kind() == reflect.Uint8 {
 			return d.decodeBytes(b, p)
 		}
-		return inputError(b, t)
+		return d.inputError(b, t)
 	}
 
 	input := b
@@ -630,7 +630,7 @@ func (d decoder) decodeSlice(b []byte, p unsafe.Pointer, size uintptr, t reflect
 
 		b, err = decode(d, b, unsafe.Pointer(uintptr(s.data)+(uintptr(s.len)*size)))
 		if err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -653,7 +653,7 @@ func (d decoder) decodeMap(b []byte, p unsafe.Pointer, t, kt, vt reflect.Type, k
 	}
 
 	if len(b) < 2 || b[0] != '{' {
-		return inputError(b, t)
+		return d.inputError(b, t)
 	}
 	i := 0
 	m := reflect.NewAt(t, p).Elem()
@@ -709,7 +709,7 @@ func (d decoder) decodeMap(b []byte, p unsafe.Pointer, t, kt, vt reflect.Type, k
 		b = skipSpaces(b[1:])
 
 		if b, err = decodeValue(d, b, vptr); err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -733,7 +733,7 @@ func (d decoder) decodeMapStringInterface(b []byte, p unsafe.Pointer) ([]byte, e
 	}
 
 	if len(b) < 2 || b[0] != '{' {
-		return inputError(b, mapStringInterfaceType)
+		return d.inputError(b, mapStringInterfaceType)
 	}
 
 	i := 0
@@ -790,7 +790,7 @@ func (d decoder) decodeMapStringInterface(b []byte, p unsafe.Pointer) ([]byte, e
 
 		b, err = d.decodeInterface(b, unsafe.Pointer(&val))
 		if err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -814,7 +814,7 @@ func (d decoder) decodeMapStringRawMessage(b []byte, p unsafe.Pointer) ([]byte,
 	}
 
 	if len(b) < 2 || b[0] != '{' {
-		return inputError(b, mapStringRawMessageType)
+		return d.inputError(b, mapStringRawMessageType)
 	}
 
 	i := 0
@@ -871,7 +871,7 @@ func (d decoder) decodeMapStringRawMessage(b []byte, p unsafe.Pointer) ([]byte,
 
 		b, err = d.decodeRawMessage(b, unsafe.Pointer(&val))
 		if err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -895,7 +895,7 @@ func (d decoder) decodeMapStringString(b []byte, p unsafe.Pointer) ([]byte, erro
 	}
 
 	if len(b) < 2 || b[0] != '{' {
-		return inputError(b, mapStringStringType)
+		return d.inputError(b, mapStringStringType)
 	}
 
 	i := 0
@@ -952,7 +952,7 @@ func (d decoder) decodeMapStringString(b []byte, p unsafe.Pointer) ([]byte, erro
 
 		b, err = d.decodeString(b, unsafe.Pointer(&val))
 		if err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -976,7 +976,7 @@ func (d decoder) decodeMapStringStringSlice(b []byte, p unsafe.Pointer) ([]byte,
 	}
 
 	if len(b) < 2 || b[0] != '{' {
-		return inputError(b, mapStringStringSliceType)
+		return d.inputError(b, mapStringStringSliceType)
 	}
 
 	i := 0
@@ -1034,7 +1034,7 @@ func (d decoder) decodeMapStringStringSlice(b []byte, p unsafe.Pointer) ([]byte,
 
 		b, err = d.decodeSlice(b, unsafe.Pointer(&buf), stringSize, sliceStringType, decoder.decodeString)
 		if err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -1061,7 +1061,7 @@ func (d decoder) decodeMapStringBool(b []byte, p unsafe.Pointer) ([]byte, error)
 	}
 
 	if len(b) < 2 || b[0] != '{' {
-		return inputError(b, mapStringBoolType)
+		return d.inputError(b, mapStringBoolType)
 	}
 
 	i := 0
@@ -1118,7 +1118,7 @@ func (d decoder) decodeMapStringBool(b []byte, p unsafe.Pointer) ([]byte, error)
 
 		b, err = d.decodeBool(b, unsafe.Pointer(&val))
 		if err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -1141,7 +1141,7 @@ func (d decoder) decodeStruct(b []byte, p unsafe.Pointer, st *structType) ([]byt
 	}
 
 	if len(b) < 2 || b[0] != '{' {
-		return inputError(b, st.typ)
+		return d.inputError(b, st.typ)
 	}
 
 	var err error
@@ -1176,7 +1176,7 @@ func (d decoder) decodeStruct(b []byte, p unsafe.Pointer, st *structType) ([]byt
 			return b, syntaxError(b, "cannot decode object key string from 'null' value")
 		}
 
-		k, b, _, err = parseStringUnquote(b, nil)
+		k, b, _, err = d.parseStringUnquote(b, nil)
 		if err != nil {
 			return objectKeyError(b, err)
 		}
@@ -1201,14 +1201,14 @@ func (d decoder) decodeStruct(b []byte, p unsafe.Pointer, st *structType) ([]byt
 			if (d.flags & DisallowUnknownFields) != 0 {
 				return b, fmt.Errorf("json: unknown field %q", k)
 			}
-			if _, b, err = parseValue(b); err != nil {
+			if _, b, err = d.parseValue(b); err != nil {
 				return b, err
 			}
 			continue
 		}
 
 		if b, err = f.codec.decode(d, b, unsafe.Pointer(uintptr(p)+f.offset)); err != nil {
-			if _, r, err := parseValue(input); err != nil {
+			if _, r, err := d.parseValue(input); err != nil {
 				return r, err
 			} else {
 				b = r
@@ -1277,7 +1277,7 @@ func (d decoder) decodeInterface(b []byte, p unsafe.Pointer) ([]byte, error) {
 		return b, err
 	}
 
-	v, b, err := parseValue(b)
+	v, b, err := d.parseValue(b)
 	if err != nil {
 		return b, err
 	}
@@ -1352,7 +1352,7 @@ func (d decoder) decodeMaybeEmptyInterface(b []byte, p unsafe.Pointer, t reflect
 }
 
 func (d decoder) decodeUnmarshalTypeError(b []byte, p unsafe.Pointer, t reflect.Type) ([]byte, error) {
-	v, b, err := parseValue(b)
+	v, b, err := d.parseValue(b)
 	if err != nil {
 		return b, err
 	}
@@ -1363,9 +1363,9 @@ func (d decoder) decodeUnmarshalTypeError(b []byte, p unsafe.Pointer, t reflect.
 }
 
 func (d decoder) decodeRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
-	v, r, err := parseValue(b)
+	v, r, err := d.parseValue(b)
 	if err != nil {
-		return inputError(b, rawMessageType)
+		return d.inputError(b, rawMessageType)
 	}
 
 	if (d.flags & DontCopyRawMessage) == 0 {
@@ -1377,7 +1377,7 @@ func (d decoder) decodeRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
 }
 
 func (d decoder) decodeJSONUnmarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
-	v, b, err := parseValue(b)
+	v, b, err := d.parseValue(b)
 	if err != nil {
 		return b, err
 	}
@@ -1397,20 +1397,20 @@ func (d decoder) decodeJSONUnmarshaler(b []byte, p unsafe.Pointer, t reflect.Typ
 func (d decoder) decodeTextUnmarshaler(b []byte, p unsafe.Pointer, t reflect.Type, pointer bool) ([]byte, error) {
 	var value string
 
-	v, b, err := parseValue(b)
+	v, b, err := d.parseValue(b)
 	if err != nil {
 		return b, err
 	}
 	if len(v) == 0 {
-		return inputError(v, t)
+		return d.inputError(v, t)
 	}
 
 	switch v[0] {
 	case 'n':
-		_, _, err := parseNull(v)
+		_, _, err := d.parseNull(v)
 		return b, err
 	case '"':
-		s, _, _, err := parseStringUnquote(v, nil)
+		s, _, _, err := d.parseStringUnquote(v, nil)
 		if err != nil {
 			return b, err
 		}
@@ -1444,3 +1444,14 @@ func (d decoder) prependField(key, field string) string {
 	}
 	return key
 }
+
+func (d decoder) inputError(b []byte, t reflect.Type) ([]byte, error) {
+	if len(b) == 0 {
+		return nil, unexpectedEOF(b)
+	}
+	_, r, err := d.parseValue(b)
+	if err != nil {
+		return r, err
+	}
+	return skipSpaces(r), unmarshalTypeError(b, t)
+}
diff --git a/json/encode.go b/json/encode.go
index 5f41c38..720bfdd 100644
--- a/json/encode.go
+++ b/json/encode.go
@@ -120,7 +120,8 @@ func (e encoder) encodeNumber(b []byte, p unsafe.Pointer) ([]byte, error) {
 		n = "0"
 	}
 
-	_, _, err := parseNumber(stringToBytes(string(n)))
+	d := decoder{}
+	_, _, err := d.parseNumber(stringToBytes(string(n)))
 	if err != nil {
 		return b, err
 	}
@@ -843,7 +844,8 @@ func (e encoder) encodeRawMessage(b []byte, p unsafe.Pointer) ([]byte, error) {
 		s = v
 	} else {
 		var err error
-		s, _, err = parseValue(v)
+		d := decoder{}
+		s, _, err = d.parseValue(v)
 		if err != nil {
 			return b, &UnsupportedValueError{Value: reflect.ValueOf(v), Str: err.Error()}
 		}
@@ -875,7 +877,8 @@ func (e encoder) encodeJSONMarshaler(b []byte, p unsafe.Pointer, t reflect.Type,
 		return b, err
 	}
 
-	s, _, err := parseValue(j)
+	d := decoder{}
+	s, _, err := d.parseValue(j)
 	if err != nil {
 		return b, &MarshalerError{Type: t, Err: err}
 	}
diff --git a/json/json.go b/json/json.go
index 2b4ad38..3b95e19 100644
--- a/json/json.go
+++ b/json/json.go
@@ -75,6 +75,10 @@ const (
 // applied when parsing json input.
 type ParseFlags int
 
+func (flags ParseFlags) has(f ParseFlags) bool {
+	return (flags & f) != 0
+}
+
 const (
 	// DisallowUnknownFields is a parsing flag used to prevent decoding of
 	// objects to Go struct values when a field of the input does not match
@@ -116,6 +120,16 @@ const (
 	// The zero-copy optimizations are better used in request-handler style
 	// code where none of the values are retained after the handler returns.
 	ZeroCopy = DontCopyString | DontCopyNumber | DontCopyRawMessage
+
+	// validAsciiPrint is an internal flag indicating that the input contains
+	// only valid ASCII print chars (0x20 <= c <= 0x7E). If the flag is unset,
+	// it's unknown whether the input is valid ASCII print.
+	validAsciiPrint ParseFlags = 1 << 28
+
+	// noBackslach is an internal flag indicating that the input does not
+	// contain a backslash. If the flag is unset, it's unknown whether the
+	// input contains a backslash.
+	noBackslash ParseFlags = 1 << 29
 )
 
 // Append acts like Marshal but appends the json representation to b instead of
@@ -245,8 +259,12 @@ func Parse(b []byte, x interface{}, flags ParseFlags) ([]byte, error) {
 	t := reflect.TypeOf(x)
 	p := (*iface)(unsafe.Pointer(&x)).ptr
 
+	d := decoder{flags: flags | internalParseFlags(b)}
+
+	b = skipSpaces(b)
+
 	if t == nil || p == nil || t.Kind() != reflect.Ptr {
-		_, r, err := parseValue(skipSpaces(b))
+		_, r, err := d.parseValue(b)
 		r = skipSpaces(r)
 		if err != nil {
 			return r, err
@@ -262,13 +280,15 @@ func Parse(b []byte, x interface{}, flags ParseFlags) ([]byte, error) {
 		c = constructCachedCodec(t, cache)
 	}
 
-	r, err := c.decode(decoder{flags: flags}, skipSpaces(b), p)
+	r, err := c.decode(d, b, p)
 	return skipSpaces(r), err
 }
 
 // Valid is documented at https://golang.org/pkg/encoding/json/#Valid
 func Valid(data []byte) bool {
-	_, data, err := parseValue(skipSpaces(data))
+	data = skipSpaces(data)
+	d := decoder{flags: internalParseFlags(data)}
+	_, data, err := d.parseValue(data)
 	if err != nil {
 		return false
 	}
@@ -313,10 +333,11 @@ const (
 func (dec *Decoder) readValue() (v []byte, err error) {
 	var n int
 	var r []byte
+	d := decoder{flags: dec.flags}
 
 	for {
 		if len(dec.remain) != 0 {
-			v, r, err = parseValue(dec.remain)
+			v, r, err = d.parseValue(dec.remain)
 			if err == nil {
 				dec.remain, n = skipSpacesN(r)
 				dec.inputOffset += int64(len(v) + n)
@@ -360,6 +381,7 @@ func (dec *Decoder) readValue() (v []byte, err error) {
 			err = io.EOF
 		}
 		dec.remain, n = skipSpacesN(dec.buffer)
+		d.flags = dec.flags | internalParseFlags(dec.remain)
 		dec.inputOffset += int64(n)
 		dec.err = err
 	}
diff --git a/json/parse.go b/json/parse.go
index 84469a7..f7f26b4 100644
--- a/json/parse.go
+++ b/json/parse.go
@@ -24,6 +24,19 @@ const (
 	quote  = '"'
 )
 
+func internalParseFlags(b []byte) (flags ParseFlags) {
+	// Don't consider surrounding whitespace
+	b = skipSpaces(b)
+	b = trimTrailingSpaces(b)
+	if ascii.ValidPrint(b) {
+		flags |= validAsciiPrint
+	}
+	if bytes.IndexByte(b, '\\') == -1 {
+		flags |= noBackslash
+	}
+	return
+}
+
 func skipSpaces(b []byte) []byte {
 	if len(b) > 0 && b[0] <= 0x20 {
 		b, _ = skipSpacesN(b)
@@ -42,6 +55,26 @@ func skipSpacesN(b []byte) ([]byte, int) {
 	return nil, 0
 }
 
+func trimTrailingSpaces(b []byte) []byte {
+	if len(b) > 0 && b[len(b)-1] <= 0x20 {
+		b = trimTrailingSpacesN(b)
+	}
+	return b
+}
+
+func trimTrailingSpacesN(b []byte) []byte {
+	i := len(b) - 1
+loop:
+	for ; i >= 0; i-- {
+		switch b[i] {
+		case sp, ht, nl, cr:
+		default:
+			break loop
+		}
+	}
+	return b[:i+1]
+}
+
 // parseInt parses a decimal representation of an int64 from b.
 //
 // The function is equivalent to calling strconv.ParseInt(string(b), 10, 64) but
@@ -50,7 +83,7 @@ func skipSpacesN(b []byte) ([]byte, int) {
 //
 // Because it only works with base 10 the function is also significantly faster
 // than strconv.ParseInt.
-func parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
+func (d decoder) parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
 	var value int64
 	var count int
 
@@ -70,10 +103,10 @@ func parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
 			return 0, b, syntaxError(b, "invalid leading character '0' in integer")
 		}
 
-		for _, d := range b[1:] {
-			if !(d >= '0' && d <= '9') {
+		for _, c := range b[1:] {
+			if !(c >= '0' && c <= '9') {
 				if count == 0 {
-					b, err := inputError(b, t)
+					b, err := d.inputError(b, t)
 					return 0, b, err
 				}
 				break
@@ -84,7 +117,7 @@ func parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
 			}
 
 			value *= 10
-			x := int64(d - '0')
+			x := int64(c - '0')
 
 			if value < (max + x) {
 				return 0, b, unmarshalOverflow(b, t)
@@ -103,15 +136,15 @@ func parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
 			return 0, b, syntaxError(b, "invalid leading character '0' in integer")
 		}
 
-		for _, d := range b {
-			if !(d >= '0' && d <= '9') {
+		for _, c := range b {
+			if !(c >= '0' && c <= '9') {
 				if count == 0 {
-					b, err := inputError(b, t)
+					b, err := d.inputError(b, t)
 					return 0, b, err
 				}
 				break
 			}
-			x := int64(d - '0')
+			x := int64(c - '0')
 
 			if value > lim {
 				return 0, b, unmarshalOverflow(b, t)
@@ -129,7 +162,7 @@ func parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
 	if count < len(b) {
 		switch b[count] {
 		case '.', 'e', 'E': // was this actually a float?
-			v, r, err := parseNumber(b)
+			v, r, err := d.parseNumber(b)
 			if err != nil {
 				v, r = b[:count+1], b[count+1:]
 			}
@@ -141,7 +174,7 @@ func parseInt(b []byte, t reflect.Type) (int64, []byte, error) {
 }
 
 // parseUint is like parseInt but for unsigned integers.
-func parseUint(b []byte, t reflect.Type) (uint64, []byte, error) {
+func (d decoder) parseUint(b []byte, t reflect.Type) (uint64, []byte, error) {
 	const max = math.MaxUint64
 	const lim = max / 10
 
@@ -156,15 +189,15 @@ func parseUint(b []byte, t reflect.Type) (uint64, []byte, error) {
 		return 0, b, syntaxError(b, "invalid leading character '0' in integer")
 	}
 
-	for _, d := range b {
-		if !(d >= '0' && d <= '9') {
+	for _, c := range b {
+		if !(c >= '0' && c <= '9') {
 			if count == 0 {
-				b, err := inputError(b, t)
+				b, err := d.inputError(b, t)
 				return 0, b, err
 			}
 			break
 		}
-		x := uint64(d - '0')
+		x := uint64(c - '0')
 
 		if value > lim {
 			return 0, b, unmarshalOverflow(b, t)
@@ -181,7 +214,7 @@ func parseUint(b []byte, t reflect.Type) (uint64, []byte, error) {
 	if count < len(b) {
 		switch b[count] {
 		case '.', 'e', 'E': // was this actually a float?
-			v, r, err := parseNumber(b)
+			v, r, err := d.parseNumber(b)
 			if err != nil {
 				v, r = b[:count+1], b[count+1:]
 			}
@@ -200,7 +233,7 @@ func parseUint(b []byte, t reflect.Type) (uint64, []byte, error) {
 //
 // Because it only works with base 16 the function is also significantly faster
 // than strconv.ParseUint.
-func parseUintHex(b []byte) (uint64, []byte, error) {
+func (d decoder) parseUintHex(b []byte) (uint64, []byte, error) {
 	const max = math.MaxUint64
 	const lim = max / 0x10
 
@@ -212,22 +245,22 @@ func parseUintHex(b []byte) (uint64, []byte, error) {
 	}
 
 parseLoop:
-	for i, d := range b {
+	for i, c := range b {
 		var x uint64
 
 		switch {
-		case d >= '0' && d <= '9':
-			x = uint64(d - '0')
+		case c >= '0' && c <= '9':
+			x = uint64(c - '0')
 
-		case d >= 'A' && d <= 'F':
-			x = uint64(d-'A') + 0xA
+		case c >= 'A' && c <= 'F':
+			x = uint64(c-'A') + 0xA
 
-		case d >= 'a' && d <= 'f':
-			x = uint64(d-'a') + 0xA
+		case c >= 'a' && c <= 'f':
+			x = uint64(c-'a') + 0xA
 
 		default:
 			if i == 0 {
-				return 0, b, syntaxError(b, "expected hexadecimal digit but found '%c'", d)
+				return 0, b, syntaxError(b, "expected hexadecimal digit but found '%c'", c)
 			}
 			break parseLoop
 		}
@@ -247,7 +280,7 @@ parseLoop:
 	return value, b[count:], nil
 }
 
-func parseNull(b []byte) ([]byte, []byte, error) {
+func (d decoder) parseNull(b []byte) ([]byte, []byte, error) {
 	if hasNullPrefix(b) {
 		return b[:4], b[4:], nil
 	}
@@ -257,7 +290,7 @@ func parseNull(b []byte) ([]byte, []byte, error) {
 	return nil, b, syntaxError(b, "expected 'null' but found invalid token")
 }
 
-func parseTrue(b []byte) ([]byte, []byte, error) {
+func (d decoder) parseTrue(b []byte) ([]byte, []byte, error) {
 	if hasTruePrefix(b) {
 		return b[:4], b[4:], nil
 	}
@@ -267,7 +300,7 @@ func parseTrue(b []byte) ([]byte, []byte, error) {
 	return nil, b, syntaxError(b, "expected 'true' but found invalid token")
 }
 
-func parseFalse(b []byte) ([]byte, []byte, error) {
+func (d decoder) parseFalse(b []byte) ([]byte, []byte, error) {
 	if hasFalsePrefix(b) {
 		return b[:5], b[5:], nil
 	}
@@ -277,7 +310,7 @@ func parseFalse(b []byte) ([]byte, []byte, error) {
 	return nil, b, syntaxError(b, "expected 'false' but found invalid token")
 }
 
-func parseNumber(b []byte) (v, r []byte, err error) {
+func (d decoder) parseNumber(b []byte) (v, r []byte, err error) {
 	if len(b) == 0 {
 		r, err = b, unexpectedEOF(b)
 		return
@@ -371,12 +404,12 @@ func parseNumber(b []byte) (v, r []byte, err error) {
 	return
 }
 
-func parseUnicode(b []byte) (rune, int, error) {
+func (d decoder) parseUnicode(b []byte) (rune, int, error) {
 	if len(b) < 4 {
 		return 0, len(b), syntaxError(b, "unicode code point must have at least 4 characters")
 	}
 
-	u, r, err := parseUintHex(b[:4])
+	u, r, err := d.parseUintHex(b[:4])
 	if err != nil {
 		return 0, 4, syntaxError(b, "parsing unicode code point: %s", err)
 	}
@@ -388,7 +421,7 @@ func parseUnicode(b []byte) (rune, int, error) {
 	return rune(u), 4, nil
 }
 
-func parseStringFast(b []byte) ([]byte, []byte, bool, error) {
+func (d decoder) parseStringFast(b []byte) ([]byte, []byte, bool, error) {
 	if len(b) < 2 {
 		return nil, b[len(b):], false, unexpectedEOF(b)
 	}
@@ -400,7 +433,8 @@ func parseStringFast(b []byte) ([]byte, []byte, bool, error) {
 	if n <= 1 {
 		return nil, b[len(b):], false, syntaxError(b, "missing '\"' at the end of a string value")
 	}
-	if bytes.IndexByte(b[1:n], '\\') < 0 && ascii.ValidPrint(b[1:n]) {
+	if (d.flags.has(noBackslash) || bytes.IndexByte(b[1:n], '\\') < 0) &&
+		(d.flags.has(validAsciiPrint) || ascii.ValidPrint(b[1:n])) {
 		return b[:n], b[n:], false, nil
 	}
 
@@ -411,7 +445,7 @@ func parseStringFast(b []byte) ([]byte, []byte, bool, error) {
 				switch b[i] {
 				case '"', '\\', '/', 'n', 'r', 't', 'f', 'b':
 				case 'u':
-					_, n, err := parseUnicode(b[i+1:])
+					_, n, err := d.parseUnicode(b[i+1:])
 					if err != nil {
 						return nil, b[i+1+n:], false, err
 					}
@@ -434,13 +468,13 @@ func parseStringFast(b []byte) ([]byte, []byte, bool, error) {
 	return nil, b[len(b):], false, syntaxError(b, "missing '\"' at the end of a string value")
 }
 
-func parseString(b []byte) ([]byte, []byte, error) {
-	s, b, _, err := parseStringFast(b)
+func (d decoder) parseString(b []byte) ([]byte, []byte, error) {
+	s, b, _, err := d.parseStringFast(b)
 	return s, b, err
 }
 
-func parseStringUnquote(b []byte, r []byte) ([]byte, []byte, bool, error) {
-	s, b, escaped, err := parseStringFast(b)
+func (d decoder) parseStringUnquote(b []byte, r []byte) ([]byte, []byte, bool, error) {
+	s, b, escaped, err := d.parseStringFast(b)
 	if err != nil {
 		return s, b, false, err
 	}
@@ -488,7 +522,7 @@ func parseStringUnquote(b []byte, r []byte) ([]byte, []byte, bool, error) {
 		case 'u':
 			s = s[1:]
 
-			r1, n1, err := parseUnicode(s)
+			r1, n1, err := d.parseUnicode(s)
 			if err != nil {
 				return r, b, true, err
 			}
@@ -498,7 +532,7 @@ func parseStringUnquote(b []byte, r []byte) ([]byte, []byte, bool, error) {
 				if !hasPrefix(s, `\u`) {
 					r1 = unicode.ReplacementChar
 				} else {
-					r2, n2, err := parseUnicode(s[2:])
+					r2, n2, err := d.parseUnicode(s[2:])
 					if err != nil {
 						return r, b, true, err
 					}
@@ -538,7 +572,7 @@ func appendCoerceInvalidUTF8(b []byte, s []byte) []byte {
 	return b
 }
 
-func parseObject(b []byte) ([]byte, []byte, error) {
+func (d decoder) parseObject(b []byte) ([]byte, []byte, error) {
 	if len(b) < 2 {
 		return nil, b[len(b):], unexpectedEOF(b)
 	}
@@ -581,7 +615,7 @@ func parseObject(b []byte) ([]byte, []byte, error) {
 			}
 		}
 
-		_, b, err = parseString(b)
+		_, b, err = d.parseString(b)
 		if err != nil {
 			return nil, b, err
 		}
@@ -595,7 +629,7 @@ func parseObject(b []byte) ([]byte, []byte, error) {
 		}
 		b = skipSpaces(b[1:])
 
-		_, b, err = parseValue(b)
+		_, b, err = d.parseValue(b)
 		if err != nil {
 			return nil, b, err
 		}
@@ -604,7 +638,7 @@ func parseObject(b []byte) ([]byte, []byte, error) {
 	}
 }
 
-func parseArray(b []byte) ([]byte, []byte, error) {
+func (d decoder) parseArray(b []byte) ([]byte, []byte, error) {
 	if len(b) < 2 {
 		return nil, b[len(b):], unexpectedEOF(b)
 	}
@@ -647,7 +681,7 @@ func parseArray(b []byte) ([]byte, []byte, error) {
 			}
 		}
 
-		_, b, err = parseValue(b)
+		_, b, err = d.parseValue(b)
 		if err != nil {
 			return nil, b, err
 		}
@@ -656,23 +690,23 @@ func parseArray(b []byte) ([]byte, []byte, error) {
 	}
 }
 
-func parseValue(b []byte) ([]byte, []byte, error) {
+func (d decoder) parseValue(b []byte) ([]byte, []byte, error) {
 	if len(b) != 0 {
 		switch b[0] {
 		case '{':
-			return parseObject(b)
+			return d.parseObject(b)
 		case '[':
-			return parseArray(b)
+			return d.parseArray(b)
 		case '"':
-			return parseString(b)
+			return d.parseString(b)
 		case 'n':
-			return parseNull(b)
+			return d.parseNull(b)
 		case 't':
-			return parseTrue(b)
+			return d.parseTrue(b)
 		case 'f':
-			return parseFalse(b)
+			return d.parseFalse(b)
 		case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-			return parseNumber(b)
+			return d.parseNumber(b)
 		default:
 			return nil, b, syntaxError(b, "invalid character '%c' looking for beginning of value", b[0])
 		}
diff --git a/json/token.go b/json/token.go
index 9a8c2c1..5f10c6f 100644
--- a/json/token.go
+++ b/json/token.go
@@ -76,10 +76,18 @@ type Tokenizer struct {
 
 	// Stack used to track entering and leaving arrays, objects, and keys.
 	stack *stack
+
+	// Decoder used for parsing.
+	decoder decoder
 }
 
 // NewTokenizer constructs a new Tokenizer which reads its json input from b.
-func NewTokenizer(b []byte) *Tokenizer { return &Tokenizer{json: b} }
+func NewTokenizer(b []byte) *Tokenizer {
+	return &Tokenizer{
+		json:    b,
+		decoder: decoder{flags: internalParseFlags(b)},
+	}
+}
 
 // Reset erases the state of t and re-initializes it with the json input from b.
 func (t *Tokenizer) Reset(b []byte) {
@@ -100,6 +108,7 @@ func (t *Tokenizer) Reset(b []byte) {
 	t.isKey = false
 	t.json = b
 	t.stack = nil
+	t.decoder = decoder{flags: internalParseFlags(b)}
 }
 
 // Next returns a new tokenizer pointing at the next token, or the zero-value of
@@ -138,19 +147,19 @@ skipLoop:
 	switch t.json[0] {
 	case '"':
 		t.Delim = 0
-		t.Value, t.json, t.Err = parseString(t.json)
+		t.Value, t.json, t.Err = t.decoder.parseString(t.json)
 	case 'n':
 		t.Delim = 0
-		t.Value, t.json, t.Err = parseNull(t.json)
+		t.Value, t.json, t.Err = t.decoder.parseNull(t.json)
 	case 't':
 		t.Delim = 0
-		t.Value, t.json, t.Err = parseTrue(t.json)
+		t.Value, t.json, t.Err = t.decoder.parseTrue(t.json)
 	case 'f':
 		t.Delim = 0
-		t.Value, t.json, t.Err = parseFalse(t.json)
+		t.Value, t.json, t.Err = t.decoder.parseFalse(t.json)
 	case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
 		t.Delim = 0
-		t.Value, t.json, t.Err = parseNumber(t.json)
+		t.Value, t.json, t.Err = t.decoder.parseNumber(t.json)
 	case '{', '}', '[', ']', ':', ',':
 		t.Delim, t.Value, t.json = Delim(t.json[0]), t.json[:1], t.json[1:]
 	default:
@@ -254,7 +263,8 @@ func (v RawValue) Number() bool {
 
 // AppendUnquote writes the unquoted version of the string value in v into b.
 func (v RawValue) AppendUnquote(b []byte) []byte {
-	s, r, new, err := parseStringUnquote([]byte(v), b)
+	d := decoder{}
+	s, r, new, err := d.parseStringUnquote(v, b)
 	if err != nil {
 		panic(err)
 	}


Test patch:
diff --git a/json/parse_test.go b/json/parse_test.go
index 798190f..cd4083a 100644
--- a/json/parse_test.go
+++ b/json/parse_test.go
@@ -22,9 +22,10 @@ func TestParseString(t *testing.T) {
 		{`"\u0"`, ``, ``, `json: unicode code point must have at least 4 characters: 0"`},
 	}
 
+	d := decoder{}
 	for _, test := range tests {
 		t.Run(test.in, func(t *testing.T) {
-			out, ext, err := parseString([]byte(test.in))
+			out, ext, err := d.parseString([]byte(test.in))
 
 			if test.err == "" {
 				if err != nil {
@@ -68,9 +69,10 @@ func TestParseStringUnquote(t *testing.T) {
 		{`"\u0061\u0062\u0063"`, `abc`, ``},
 	}
 
+	d := decoder{}
 	for _, test := range tests {
 		t.Run(test.in, func(t *testing.T) {
-			out, ext, _, err := parseStringUnquote([]byte(test.in), nil)
+			out, ext, _, err := d.parseStringUnquote([]byte(test.in), nil)
 
 			if err != nil {
 				t.Errorf("%s => %s", test.in, err)
@@ -134,8 +136,9 @@ func TestAppendToLower(t *testing.T) {
 func BenchmarkParseString(b *testing.B) {
 	s := []byte(`"__segment_internal"`)
 
+	d := decoder{}
 	for i := 0; i != b.N; i++ {
-		parseString(s)
+		d.parseString(s)
 	}
 }