Repository: gabriel-vasile/mimetype
Pull request #652 (2025-04-08 14:19:58)

json: add parser with query capacity
Same speed but fewer allocs.


Patch:
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index 08b1c2b..d388440 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -16,7 +16,7 @@ jobs:
       with:
         ref: master
     - name: Install Go
-      uses: actions/setup-go@v5.2.0
+      uses: actions/setup-go@v5.4.0
       with:
         go-version: 'stable'
     - run: go install golang.org/x/perf/cmd/benchstat@latest
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 34797aa..a1c7f89 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -23,10 +23,10 @@ jobs:
         uses: actions/checkout@v4.2.2
 
       - name: Initialize CodeQL
-        uses: github/codeql-action/init@v3.28.0
+        uses: github/codeql-action/init@v3.28.15
         with:
           languages: go
           queries: security-and-quality
 
       - name: Perform CodeQL Analysis
-        uses: github/codeql-action/analyze@v3.28.0
+        uses: github/codeql-action/analyze@v3.28.15
diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml
new file mode 100644
index 0000000..5121b5f
--- /dev/null
+++ b/.github/workflows/fuzz.yml
@@ -0,0 +1,27 @@
+name: Fuzz
+on:
+  schedule:
+    - cron: "0 3 * * *"
+  workflow_dispatch:
+
+permissions:
+  contents: read
+
+jobs:
+  fuzz:
+    runs-on: ubuntu-latest
+    steps:
+    - name: Checkout code
+      uses: actions/checkout@v4.2.2
+    - name: Install Go
+      if: success()
+      uses: actions/setup-go@v5.4.0
+      with:
+        go-version-file: 'go.mod'
+    # 5m is chosen so that half of the 2000 free CI minutes offered by Github
+    # will be used each month. The other half is reserved for tests in PRs.
+    - run: go test -fuzztime=5m -fuzz=.
+    - run: go test -fuzztime=5m -fuzz=.     ./internal/json
+    - run: go test -fuzztime=5m -fuzz=Plain ./internal/charset
+    - run: go test -fuzztime=5m -fuzz=XML   ./internal/charset
+    - run: go test -fuzztime=5m -fuzz=HTML  ./internal/charset
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index f45b7a4..38599d7 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -1,4 +1,4 @@
-name: Run Tests
+name: Tests and linters
 on:
   push:
     branches:
@@ -9,32 +9,18 @@ permissions:
   contents: read
 
 jobs:
-  lint:
+  test:
     runs-on: ubuntu-latest
     steps:
     - name: Checkout code
       uses: actions/checkout@v4.2.2
     - name: Install Go
-      uses: actions/setup-go@v5.2.0
+      uses: actions/setup-go@v5.4.0
       with:
         go-version-file: 'go.mod'
     - name: Run linters
-      uses: golangci/golangci-lint-action@v6
-      with:
-        version: "v1.58"
-
-  test:
-    runs-on: ubuntu-latest
-    steps:
-    - name: Checkout code
-      uses: actions/checkout@v4.2.2
-    - name: Install Go
-      if: success()
-      uses: actions/setup-go@v5.2.0
+      uses: golangci/golangci-lint-action@v7
       with:
-        go-version-file: 'go.mod'
-    - run: go test -race ./...
-    - run: go test -fuzz=. -fuzztime=30s
-    - run: go test -fuzz=Plain -fuzztime=30s ./internal/charset
-    - run: go test -fuzz=XML -fuzztime=30s ./internal/charset
-    - run: go test -fuzz=HTML -fuzztime=30s ./internal/charset
+        version: "v2.0.2"
+    - name: Run tests
+      run: go test -race ./...
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000..f2058cc
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,5 @@
+version: "2"
+linters:
+  exclusions:
+    presets:
+      - std-error-handling
diff --git a/go.mod b/go.mod
index 74aed29..33fa6b4 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,7 @@ module github.com/gabriel-vasile/mimetype
 
 go 1.20
 
-require golang.org/x/net v0.33.0
+require golang.org/x/net v0.39.0
 
 // v1.4.4 had a test file detected as malicious by antivirus software. #575
 retract v1.4.4
diff --git a/go.sum b/go.sum
index 16660ab..05eec08 100644
--- a/go.sum
+++ b/go.sum
@@ -1,2 +1,2 @@
-golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
-golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
+golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
+golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
diff --git a/internal/json/json.go b/internal/json/json.go
deleted file mode 100644
index 5b2ecee..0000000
--- a/internal/json/json.go
+++ /dev/null
@@ -1,567 +0,0 @@
-// Copyright (c) 2009 The Go Authors. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//    * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-//    * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-//    * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// Package json provides a JSON value parser state machine.
-// This package is almost entirely copied from the Go stdlib.
-// Changes made to it permit users of the package to tell
-// if some slice of bytes is a valid beginning of a json string.
-package json
-
-import (
-	"fmt"
-	"sync"
-)
-
-type (
-	scanStatus int
-)
-
-const (
-	parseObjectKey   = iota // parsing object key (before colon)
-	parseObjectValue        // parsing object value (after colon)
-	parseArrayValue         // parsing array value
-
-	scanContinue     scanStatus = iota // uninteresting byte
-	scanBeginLiteral                   // end implied by next result != scanContinue
-	scanBeginObject                    // begin object
-	scanObjectKey                      // just finished object key (string)
-	scanObjectValue                    // just finished non-last object value
-	scanEndObject                      // end object (implies scanObjectValue if possible)
-	scanBeginArray                     // begin array
-	scanArrayValue                     // just finished array value
-	scanEndArray                       // end array (implies scanArrayValue if possible)
-	scanSkipSpace                      // space byte; can skip; known to be last "continue" result
-	scanEnd                            // top-level value ended *before* this byte; known to be first "stop" result
-	scanError                          // hit an error, scanner.err.
-
-	// This limits the max nesting depth to prevent stack overflow.
-	// This is permitted by https://tools.ietf.org/html/rfc7159#section-9
-	maxNestingDepth = 10000
-)
-
-type (
-	scanner struct {
-		step       func(*scanner, byte) scanStatus
-		parseState []int
-		endTop     bool
-		err        error
-		index      int
-	}
-)
-
-var scannerPool = sync.Pool{
-	New: func() any {
-		return &scanner{}
-	},
-}
-
-func newScanner() *scanner {
-	s := scannerPool.Get().(*scanner)
-	s.reset()
-	return s
-}
-
-func freeScanner(s *scanner) {
-	// Avoid hanging on to too much memory in extreme cases.
-	if len(s.parseState) > 1024 {
-		s.parseState = nil
-	}
-	scannerPool.Put(s)
-}
-
-// Scan returns the number of bytes scanned and if there was any error
-// in trying to reach the end of data.
-func Scan(data []byte) (int, error) {
-	s := newScanner()
-	defer freeScanner(s)
-	_ = checkValid(data, s)
-	return s.index, s.err
-}
-
-// checkValid verifies that data is valid JSON-encoded data.
-// scan is passed in for use by checkValid to avoid an allocation.
-func checkValid(data []byte, scan *scanner) error {
-	for _, c := range data {
-		scan.index++
-		if scan.step(scan, c) == scanError {
-			return scan.err
-		}
-	}
-	if scan.eof() == scanError {
-		return scan.err
-	}
-	return nil
-}
-
-func isSpace(c byte) bool {
-	return c == ' ' || c == '\t' || c == '\r' || c == '\n'
-}
-
-func (s *scanner) reset() {
-	s.step = stateBeginValue
-	s.parseState = s.parseState[0:0]
-	s.err = nil
-	s.endTop = false
-	s.index = 0
-}
-
-// eof tells the scanner that the end of input has been reached.
-// It returns a scan status just as s.step does.
-func (s *scanner) eof() scanStatus {
-	if s.err != nil {
-		return scanError
-	}
-	if s.endTop {
-		return scanEnd
-	}
-	s.step(s, ' ')
-	if s.endTop {
-		return scanEnd
-	}
-	if s.err == nil {
-		s.err = fmt.Errorf("unexpected end of JSON input")
-	}
-	return scanError
-}
-
-// pushParseState pushes a new parse state p onto the parse stack.
-// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned.
-func (s *scanner) pushParseState(c byte, newParseState int, successState scanStatus) scanStatus {
-	s.parseState = append(s.parseState, newParseState)
-	if len(s.parseState) <= maxNestingDepth {
-		return successState
-	}
-	return s.error(c, "exceeded max depth")
-}
-
-// popParseState pops a parse state (already obtained) off the stack
-// and updates s.step accordingly.
-func (s *scanner) popParseState() {
-	n := len(s.parseState) - 1
-	s.parseState = s.parseState[0:n]
-	if n == 0 {
-		s.step = stateEndTop
-		s.endTop = true
-	} else {
-		s.step = stateEndValue
-	}
-}
-
-// stateBeginValueOrEmpty is the state after reading `[`.
-func stateBeginValueOrEmpty(s *scanner, c byte) scanStatus {
-	if c <= ' ' && isSpace(c) {
-		return scanSkipSpace
-	}
-	if c == ']' {
-		return stateEndValue(s, c)
-	}
-	return stateBeginValue(s, c)
-}
-
-// stateBeginValue is the state at the beginning of the input.
-func stateBeginValue(s *scanner, c byte) scanStatus {
-	if c <= ' ' && isSpace(c) {
-		return scanSkipSpace
-	}
-	switch c {
-	case '{':
-		s.step = stateBeginStringOrEmpty
-		return s.pushParseState(c, parseObjectKey, scanBeginObject)
-	case '[':
-		s.step = stateBeginValueOrEmpty
-		return s.pushParseState(c, parseArrayValue, scanBeginArray)
-	case '"':
-		s.step = stateInString
-		return scanBeginLiteral
-	case '-':
-		s.step = stateNeg
-		return scanBeginLiteral
-	case '0': // beginning of 0.123
-		s.step = state0
-		return scanBeginLiteral
-	case 't': // beginning of true
-		s.step = stateT
-		return scanBeginLiteral
-	case 'f': // beginning of false
-		s.step = stateF
-		return scanBeginLiteral
-	case 'n': // beginning of null
-		s.step = stateN
-		return scanBeginLiteral
-	}
-	if '1' <= c && c <= '9' { // beginning of 1234.5
-		s.step = state1
-		return scanBeginLiteral
-	}
-	return s.error(c, "looking for beginning of value")
-}
-
-// stateBeginStringOrEmpty is the state after reading `{`.
-func stateBeginStringOrEmpty(s *scanner, c byte) scanStatus {
-	if c <= ' ' && isSpace(c) {
-		return scanSkipSpace
-	}
-	if c == '}' {
-		n := len(s.parseState)
-		s.parseState[n-1] = parseObjectValue
-		return stateEndValue(s, c)
-	}
-	return stateBeginString(s, c)
-}
-
-// stateBeginString is the state after reading `{"key": value,`.
-func stateBeginString(s *scanner, c byte) scanStatus {
-	if c <= ' ' && isSpace(c) {
-		return scanSkipSpace
-	}
-	if c == '"' {
-		s.step = stateInString
-		return scanBeginLiteral
-	}
-	return s.error(c, "looking for beginning of object key string")
-}
-
-// stateEndValue is the state after completing a value,
-// such as after reading `{}` or `true` or `["x"`.
-func stateEndValue(s *scanner, c byte) scanStatus {
-	n := len(s.parseState)
-	if n == 0 {
-		// Completed top-level before the current byte.
-		s.step = stateEndTop
-		s.endTop = true
-		return stateEndTop(s, c)
-	}
-	if c <= ' ' && isSpace(c) {
-		s.step = stateEndValue
-		return scanSkipSpace
-	}
-	ps := s.parseState[n-1]
-	switch ps {
-	case parseObjectKey:
-		if c == ':' {
-			s.parseState[n-1] = parseObjectValue
-			s.step = stateBeginValue
-			return scanObjectKey
-		}
-		return s.error(c, "after object key")
-	case parseObjectValue:
-		if c == ',' {
-			s.parseState[n-1] = parseObjectKey
-			s.step = stateBeginString
-			return scanObjectValue
-		}
-		if c == '}' {
-			s.popParseState()
-			return scanEndObject
-		}
-		return s.error(c, "after object key:value pair")
-	case parseArrayValue:
-		if c == ',' {
-			s.step = stateBeginValue
-			return scanArrayValue
-		}
-		if c == ']' {
-			s.popParseState()
-			return scanEndArray
-		}
-		return s.error(c, "after array element")
-	}
-	return s.error(c, "")
-}
-
-// stateEndTop is the state after finishing the top-level value,
-// such as after reading `{}` or `[1,2,3]`.
-// Only space characters should be seen now.
-func stateEndTop(s *scanner, c byte) scanStatus {
-	if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
-		// Complain about non-space byte on next call.
-		s.error(c, "after top-level value")
-	}
-	return scanEnd
-}
-
-// stateInString is the state after reading `"`.
-func stateInString(s *scanner, c byte) scanStatus {
-	if c == '"' {
-		s.step = stateEndValue
-		return scanContinue
-	}
-	if c == '\\' {
-		s.step = stateInStringEsc
-		return scanContinue
-	}
-	if c < 0x20 {
-		return s.error(c, "in string literal")
-	}
-	return scanContinue
-}
-
-// stateInStringEsc is the state after reading `"\` during a quoted string.
-func stateInStringEsc(s *scanner, c byte) scanStatus {
-	switch c {
-	case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
-		s.step = stateInString
-		return scanContinue
-	case 'u':
-		s.step = stateInStringEscU
-		return scanContinue
-	}
-	return s.error(c, "in string escape code")
-}
-
-// stateInStringEscU is the state after reading `"\u` during a quoted string.
-func stateInStringEscU(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
-		s.step = stateInStringEscU1
-		return scanContinue
-	}
-	// numbers
-	return s.error(c, "in \\u hexadecimal character escape")
-}
-
-// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
-func stateInStringEscU1(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
-		s.step = stateInStringEscU12
-		return scanContinue
-	}
-	// numbers
-	return s.error(c, "in \\u hexadecimal character escape")
-}
-
-// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
-func stateInStringEscU12(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
-		s.step = stateInStringEscU123
-		return scanContinue
-	}
-	// numbers
-	return s.error(c, "in \\u hexadecimal character escape")
-}
-
-// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
-func stateInStringEscU123(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
-		s.step = stateInString
-		return scanContinue
-	}
-	// numbers
-	return s.error(c, "in \\u hexadecimal character escape")
-}
-
-// stateNeg is the state after reading `-` during a number.
-func stateNeg(s *scanner, c byte) scanStatus {
-	if c == '0' {
-		s.step = state0
-		return scanContinue
-	}
-	if '1' <= c && c <= '9' {
-		s.step = state1
-		return scanContinue
-	}
-	return s.error(c, "in numeric literal")
-}
-
-// state1 is the state after reading a non-zero integer during a number,
-// such as after reading `1` or `100` but not `0`.
-func state1(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' {
-		s.step = state1
-		return scanContinue
-	}
-	return state0(s, c)
-}
-
-// state0 is the state after reading `0` during a number.
-func state0(s *scanner, c byte) scanStatus {
-	if c == '.' {
-		s.step = stateDot
-		return scanContinue
-	}
-	if c == 'e' || c == 'E' {
-		s.step = stateE
-		return scanContinue
-	}
-	return stateEndValue(s, c)
-}
-
-// stateDot is the state after reading the integer and decimal point in a number,
-// such as after reading `1.`.
-func stateDot(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' {
-		s.step = stateDot0
-		return scanContinue
-	}
-	return s.error(c, "after decimal point in numeric literal")
-}
-
-// stateDot0 is the state after reading the integer, decimal point, and subsequent
-// digits of a number, such as after reading `3.14`.
-func stateDot0(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' {
-		return scanContinue
-	}
-	if c == 'e' || c == 'E' {
-		s.step = stateE
-		return scanContinue
-	}
-	return stateEndValue(s, c)
-}
-
-// stateE is the state after reading the mantissa and e in a number,
-// such as after reading `314e` or `0.314e`.
-func stateE(s *scanner, c byte) scanStatus {
-	if c == '+' || c == '-' {
-		s.step = stateESign
-		return scanContinue
-	}
-	return stateESign(s, c)
-}
-
-// stateESign is the state after reading the mantissa, e, and sign in a number,
-// such as after reading `314e-` or `0.314e+`.
-func stateESign(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' {
-		s.step = stateE0
-		return scanContinue
-	}
-	return s.error(c, "in exponent of numeric literal")
-}
-
-// stateE0 is the state after reading the mantissa, e, optional sign,
-// and at least one digit of the exponent in a number,
-// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
-func stateE0(s *scanner, c byte) scanStatus {
-	if '0' <= c && c <= '9' {
-		return scanContinue
-	}
-	return stateEndValue(s, c)
-}
-
-// stateT is the state after reading `t`.
-func stateT(s *scanner, c byte) scanStatus {
-	if c == 'r' {
-		s.step = stateTr
-		return scanContinue
-	}
-	return s.error(c, "in literal true (expecting 'r')")
-}
-
-// stateTr is the state after reading `tr`.
-func stateTr(s *scanner, c byte) scanStatus {
-	if c == 'u' {
-		s.step = stateTru
-		return scanContinue
-	}
-	return s.error(c, "in literal true (expecting 'u')")
-}
-
-// stateTru is the state after reading `tru`.
-func stateTru(s *scanner, c byte) scanStatus {
-	if c == 'e' {
-		s.step = stateEndValue
-		return scanContinue
-	}
-	return s.error(c, "in literal true (expecting 'e')")
-}
-
-// stateF is the state after reading `f`.
-func stateF(s *scanner, c byte) scanStatus {
-	if c == 'a' {
-		s.step = stateFa
-		return scanContinue
-	}
-	return s.error(c, "in literal false (expecting 'a')")
-}
-
-// stateFa is the state after reading `fa`.
-func stateFa(s *scanner, c byte) scanStatus {
-	if c == 'l' {
-		s.step = stateFal
-		return scanContinue
-	}
-	return s.error(c, "in literal false (expecting 'l')")
-}
-
-// stateFal is the state after reading `fal`.
-func stateFal(s *scanner, c byte) scanStatus {
-	if c == 's' {
-		s.step = stateFals
-		return scanContinue
-	}
-	return s.error(c, "in literal false (expecting 's')")
-}
-
-// stateFals is the state after reading `fals`.
-func stateFals(s *scanner, c byte) scanStatus {
-	if c == 'e' {
-		s.step = stateEndValue
-		return scanContinue
-	}
-	return s.error(c, "in literal false (expecting 'e')")
-}
-
-// stateN is the state after reading `n`.
-func stateN(s *scanner, c byte) scanStatus {
-	if c == 'u' {
-		s.step = stateNu
-		return scanContinue
-	}
-	return s.error(c, "in literal null (expecting 'u')")
-}
-
-// stateNu is the state after reading `nu`.
-func stateNu(s *scanner, c byte) scanStatus {
-	if c == 'l' {
-		s.step = stateNul
-		return scanContinue
-	}
-	return s.error(c, "in literal null (expecting 'l')")
-}
-
-// stateNul is the state after reading `nul`.
-func stateNul(s *scanner, c byte) scanStatus {
-	if c == 'l' {
-		s.step = stateEndValue
-		return scanContinue
-	}
-	return s.error(c, "in literal null (expecting 'l')")
-}
-
-// stateError is the state after reaching a syntax error,
-// such as after reading `[1}` or `5.1.2`.
-func stateError(s *scanner, c byte) scanStatus {
-	return scanError
-}
-
-// error records an error and switches to the error state.
-func (s *scanner) error(c byte, context string) scanStatus {
-	s.step = stateError
-	s.err = fmt.Errorf("invalid character <<%c>> %s", c, context)
-	return scanError
-}
diff --git a/internal/json/parser.go b/internal/json/parser.go
new file mode 100644
index 0000000..8376feb
--- /dev/null
+++ b/internal/json/parser.go
@@ -0,0 +1,459 @@
+package json
+
+import (
+	"bytes"
+	"sync"
+)
+
+const (
+	QueryNone    = "json"
+	QueryGeoJSON = "geo"
+	QueryHARJSON = "har"
+	maxRecursion = 4096
+)
+
+var queries = map[string][]query{
+	QueryNone: nil,
+	QueryGeoJSON: {{
+		SearchPath: [][]byte{[]byte("type")},
+		SearchVals: [][]byte{
+			[]byte(`"Feature"`),
+			[]byte(`"FeatureCollection"`),
+			[]byte(`"Point"`),
+			[]byte(`"LineString"`),
+			[]byte(`"Polygon"`),
+			[]byte(`"MultiPoint"`),
+			[]byte(`"MultiLineString"`),
+			[]byte(`"MultiPolygon"`),
+			[]byte(`"GeometryCollection"`),
+		},
+	}},
+	QueryHARJSON: {{
+		SearchPath: [][]byte{[]byte("log"), []byte("version")},
+	}, {
+		SearchPath: [][]byte{[]byte("log"), []byte("creator")},
+	}, {
+		SearchPath: [][]byte{[]byte("log"), []byte("entries")},
+	}},
+}
+
+var parserPool = sync.Pool{
+	New: func() any {
+		return &parserState{maxRecursion: maxRecursion}
+	},
+}
+
+// parserState holds the state of JSON parsing. The number of inspected bytes,
+// the current path inside the JSON object, etc.
+type parserState struct {
+	// ib represents the number of inspected bytes.
+	// Because mimetype limits itself to only reading the header of the file,
+	// it means sometimes the input JSON can be truncated. In that case, we want
+	// to still detect it as JSON, even if it's invalid/truncated.
+	// When ib == len(input) it means the JSON was valid (at least the header).
+	ib           int
+	maxRecursion int
+	// currPath keeps a track of the JSON keys parsed up.
+	// It works only for JSON objects. JSON arrays are ignored
+	// mainly because the functionality is not needed.
+	currPath [][]byte
+	// firstToken stores the first JSON token encountered in input.
+	// TODO: performance would be better if we would stop parsing as soon
+	// as we see that first token is not what we are interested in.
+	firstToken int
+	// querySatisfied is true if both path and value of any queries passed to
+	// consumeAny are satisfied.
+	querySatisfied bool
+}
+
+// query holds information about a combination of {"key": "val"} that we're trying
+// to search for inside the JSON.
+type query struct {
+	// SearchPath represents the whole path to look for inside the JSON.
+	// ex: [][]byte{[]byte("foo"), []byte("bar")} matches {"foo": {"bar": "baz"}}
+	SearchPath [][]byte
+	// SearchVals represents values to look for when the SearchPath is found.
+	// Each SearchVal element is tried until one of them matches (logical OR.)
+	SearchVals [][]byte
+}
+
+func eq(path1, path2 [][]byte) bool {
+	if len(path1) != len(path2) {
+		return false
+	}
+	for i := range path1 {
+		if !bytes.Equal(path1[i], path2[i]) {
+			return false
+		}
+	}
+	return true
+}
+
+// LooksLikeObjectOrArray reports if first non white space character from raw
+// is either { or [. Parsing raw as JSON is a heavy operation. When receiving some
+// text input we can skip parsing if the input does not even look like JSON.
+func LooksLikeObjectOrArray(raw []byte) bool {
+	for i := range raw {
+		if isSpace(raw[i]) {
+			continue
+		}
+		return raw[i] == '{' || raw[i] == '['
+	}
+
+	return false
+}
+
+// Parse will take out a parser from the pool depending on queryType and tries
+// to parse raw bytes as JSON.
+func Parse(queryType string, raw []byte) (parsed, inspected, firstToken int, querySatisfied bool) {
+	p := parserPool.Get().(*parserState)
+	defer func() {
+		// Avoid hanging on to too much memory in extreme input cases.
+		if len(p.currPath) > 128 {
+			p.currPath = nil
+		}
+		parserPool.Put(p)
+	}()
+	p.reset()
+
+	qs := queries[queryType]
+	got := p.consumeAny(raw, qs, 0)
+	return got, p.ib, p.firstToken, p.querySatisfied
+}
+
+func (p *parserState) reset() {
+	p.ib = 0
+	p.currPath = p.currPath[0:0]
+	p.firstToken = TokInvalid
+	p.querySatisfied = false
+}
+
+func (p *parserState) consumeSpace(b []byte) (n int) {
+	for len(b) > 0 && isSpace(b[0]) {
+		b = b[1:]
+		n++
+		p.ib++
+	}
+	return n
+}
+
+func (p *parserState) consumeConst(b, cnst []byte) int {
+	lb := len(b)
+	for i, c := range cnst {
+		if lb > i && b[i] == c {
+			p.ib++
+		} else {
+			return 0
+		}
+	}
+	return len(cnst)
+}
+
+func (p *parserState) consumeString(b []byte) (n int) {
+	var c byte
+	for len(b[n:]) > 0 {
+		c, n = b[n], n+1
+		p.ib++
+		switch c {
+		case '\\':
+			if len(b[n:]) == 0 {
+				return 0
+			}
+			switch b[n] {
+			case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
+				n++
+				p.ib++
+				continue
+			case 'u':
+				n++
+				p.ib++
+				for j := 0; j < 4 && len(b[n:]) > 0; j++ {
+					if !isXDigit(b[n]) {
+						return 0
+					}
+					n++
+					p.ib++
+				}
+				continue
+			default:
+				return 0
+			}
+		case '"':
+			return n
+		default:
+			continue
+		}
+	}
+	return 0
+}
+
+func (p *parserState) consumeNumber(b []byte) (n int) {
+	got := false
+	var i int
+
+	if len(b) == 0 {
+		goto out
+	}
+	if b[0] == '-' {
+		b, i = b[1:], i+1
+		p.ib++
+	}
+
+	for len(b) > 0 {
+		if !isDigit(b[0]) {
+			break
+		}
+		got = true
+		b, i = b[1:], i+1
+		p.ib++
+	}
+	if len(b) == 0 {
+		goto out
+	}
+	if b[0] == '.' {
+		b, i = b[1:], i+1
+		p.ib++
+	}
+	for len(b) > 0 {
+		if !isDigit(b[0]) {
+			break
+		}
+		got = true
+		b, i = b[1:], i+1
+		p.ib++
+	}
+	if len(b) == 0 {
+		goto out
+	}
+	if got && (b[0] == 'e' || b[0] == 'E') {
+		b, i = b[1:], i+1
+		p.ib++
+		got = false
+		if len(b) == 0 {
+			goto out
+		}
+		if b[0] == '+' || b[0] == '-' {
+			b, i = b[1:], i+1
+			p.ib++
+		}
+		for len(b) > 0 {
+			if !isDigit(b[0]) {
+				break
+			}
+			got = true
+			b, i = b[1:], i+1
+			p.ib++
+		}
+	}
+out:
+	if got {
+		return i
+	}
+	return 0
+}
+
+func (p *parserState) consumeArray(b []byte, qs []query, lvl int) (n int) {
+	p.currPath = append(p.currPath, []byte{'['})
+	if len(b) == 0 {
+		return 0
+	}
+
+	for n < len(b) {
+		n += p.consumeSpace(b[n:])
+		if len(b[n:]) == 0 {
+			return 0
+		}
+		if b[n] == ']' {
+			p.ib++
+			p.currPath = p.currPath[:len(p.currPath)-1]
+			return n + 1
+		}
+		innerParsed := p.consumeAny(b[n:], qs, lvl)
+		if innerParsed == 0 {
+			return 0
+		}
+		n += innerParsed
+		if len(b[n:]) == 0 {
+			return 0
+		}
+		switch b[n] {
+		case ',':
+			n += 1
+			p.ib++
+			continue
+		case ']':
+			p.ib++
+			return n + 1
+		default:
+			return 0
+		}
+	}
+	return 0
+}
+
+func queryPathMatch(qs []query, path [][]byte) int {
+	for i := range qs {
+		if eq(qs[i].SearchPath, path) {
+			return i
+		}
+	}
+	return -1
+}
+
+func (p *parserState) consumeObject(b []byte, qs []query, lvl int) (n int) {
+	for n < len(b) {
+		n += p.consumeSpace(b[n:])
+		if len(b[n:]) == 0 {
+			return 0
+		}
+		if b[n] == '}' {
+			p.ib++
+			return n + 1
+		}
+		if b[n] != '"' {
+			return 0
+		} else {
+			n += 1
+			p.ib++
+		}
+		// queryMatched stores the index of the query satisfying the current path.
+		queryMatched := -1
+		if keyLen := p.consumeString(b[n:]); keyLen == 0 {
+			return 0
+		} else {
+			p.currPath = append(p.currPath, b[n:n+keyLen-1])
+			if !p.querySatisfied {
+				queryMatched = queryPathMatch(qs, p.currPath)
+			}
+			n += keyLen
+		}
+		n += p.consumeSpace(b[n:])
+		if len(b[n:]) == 0 {
+			return 0
+		}
+		if b[n] != ':' {
+			return 0
+		} else {
+			n += 1
+			p.ib++
+		}
+		n += p.consumeSpace(b[n:])
+		if len(b[n:]) == 0 {
+			return 0
+		}
+
+		if valLen := p.consumeAny(b[n:], qs, lvl); valLen == 0 {
+			return 0
+		} else {
+			if queryMatched != -1 {
+				q := qs[queryMatched]
+				if len(q.SearchVals) == 0 {
+					p.querySatisfied = true
+				}
+				for _, val := range q.SearchVals {
+					if bytes.Equal(val, bytes.TrimSpace(b[n:n+valLen])) {
+						p.querySatisfied = true
+					}
+				}
+			}
+			n += valLen
+		}
+		if len(b[n:]) == 0 {
+			return 0
+		}
+		switch b[n] {
+		case ',':
+			p.currPath = p.currPath[:len(p.currPath)-1]
+			n++
+			p.ib++
+			continue
+		case '}':
+			p.currPath = p.currPath[:len(p.currPath)-1]
+			p.ib++
+			return n + 1
+		default:
+			return 0
+		}
+	}
+	return 0
+}
+
+func (p *parserState) consumeAny(b []byte, qs []query, lvl int) (n int) {
+	// Avoid too much recursion.
+	if p.maxRecursion != 0 && lvl > p.maxRecursion {
+		return 0
+	}
+	n += p.consumeSpace(b)
+	if len(b[n:]) == 0 {
+		return 0
+	}
+
+	var t, rv int
+	switch b[n] {
+	case '"':
+		n++
+		p.ib++
+		rv = p.consumeString(b[n:])
+		t = TokString
+	case '[':
+		n++
+		p.ib++
+		rv = p.consumeArray(b[n:], qs, lvl+1)
+		t = TokArray
+	case '{':
+		n++
+		p.ib++
+		rv = p.consumeObject(b[n:], qs, lvl+1)
+		t = TokObject
+	case 't':
+		rv = p.consumeConst(b[n:], []byte("true"))
+		t = TokTrue
+	case 'f':
+		rv = p.consumeConst(b[n:], []byte("false"))
+		t = TokFalse
+	case 'n':
+		rv = p.consumeConst(b[n:], []byte("null"))
+		t = TokNull
+	default:
+		rv = p.consumeNumber(b[n:])
+		t = TokNumber
+	}
+	if lvl == 0 {
+		p.firstToken = t
+	}
+	if len(qs) == 0 {
+		p.querySatisfied = true
+	}
+	if rv <= 0 {
+		return n
+	}
+	n += rv
+	n += p.consumeSpace(b[n:])
+	return n
+}
+
+func isSpace(c byte) bool {
+	return c == ' ' || c == '\t' || c == '\r' || c == '\n'
+}
+func isDigit(c byte) bool {
+	return '0' <= c && c <= '9'
+}
+
+func isXDigit(c byte) bool {
+	if isDigit(c) {
+		return true
+	}
+	return ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
+}
+
+const (
+	TokInvalid = 0
+	TokNull    = 1 << iota
+	TokTrue
+	TokFalse
+	TokNumber
+	TokString
+	TokArray
+	TokObject
+	TokComma
+)
diff --git a/internal/magic/archive.go b/internal/magic/archive.go
index 068d00f..dd7f241 100644
--- a/internal/magic/archive.go
+++ b/internal/magic/archive.go
@@ -137,7 +137,7 @@ func tarParseOctal(b []byte) int64 {
 		if b == 0 {
 			break
 		}
-		if !(b >= '0' && b <= '7') {
+		if b < '0' || b > '7' {
 			return -1
 		}
 		ret = (ret << 3) | int64(b-'0')
diff --git a/internal/magic/binary.go b/internal/magic/binary.go
index 7697320..0dec177 100644
--- a/internal/magic/binary.go
+++ b/internal/magic/binary.go
@@ -71,7 +71,7 @@ func Dbf(raw []byte, limit uint32) bool {
 	}
 
 	// 3rd and 4th bytes contain the last update month and day of month.
-	if !(0 < raw[2] && raw[2] < 13 && 0 < raw[3] && raw[3] < 32) {
+	if raw[2] == 0 || raw[2] > 12 || raw[3] == 0 || raw[3] > 31 {
 		return false
 	}
 
diff --git a/internal/magic/geo.go b/internal/magic/geo.go
index f077e16..cade91f 100644
--- a/internal/magic/geo.go
+++ b/internal/magic/geo.go
@@ -12,13 +12,13 @@ func Shp(raw []byte, limit uint32) bool {
 		return false
 	}
 
-	if !(binary.BigEndian.Uint32(raw[0:4]) == 9994 &&
-		binary.BigEndian.Uint32(raw[4:8]) == 0 &&
-		binary.BigEndian.Uint32(raw[8:12]) == 0 &&
-		binary.BigEndian.Uint32(raw[12:16]) == 0 &&
-		binary.BigEndian.Uint32(raw[16:20]) == 0 &&
-		binary.BigEndian.Uint32(raw[20:24]) == 0 &&
-		binary.LittleEndian.Uint32(raw[28:32]) == 1000) {
+	if binary.BigEndian.Uint32(raw[0:4]) != 9994 ||
+		binary.BigEndian.Uint32(raw[4:8]) != 0 ||
+		binary.BigEndian.Uint32(raw[8:12]) != 0 ||
+		binary.BigEndian.Uint32(raw[12:16]) != 0 ||
+		binary.BigEndian.Uint32(raw[16:20]) != 0 ||
+		binary.BigEndian.Uint32(raw[20:24]) != 0 ||
+		binary.LittleEndian.Uint32(raw[28:32]) != 1000 {
 		return false
 	}
 
diff --git a/internal/magic/text.go b/internal/magic/text.go
index 47fe7fa..0fef06b 100644
--- a/internal/magic/text.go
+++ b/internal/magic/text.go
@@ -153,145 +153,66 @@ func Php(raw []byte, limit uint32) bool {
 
 // JSON matches a JavaScript Object Notation file.
 func JSON(raw []byte, limit uint32) bool {
-	raw = trimLWS(raw)
 	// #175 A single JSON string, number or bool is not considered JSON.
 	// JSON objects and arrays are reported as JSON.
-	if len(raw) < 2 || (raw[0] != '[' && raw[0] != '{') {
-		return false
-	}
-	parsed, err := json.Scan(raw)
-	// If the full file content was provided, check there is no error.
-	if limit == 0 || len(raw) < int(limit) {
-		return err == nil
-	}
-
-	// If a section of the file was provided, check if all of it was parsed.
-	return parsed == len(raw) && len(raw) > 0
+	return jsonHelper(raw, limit, json.QueryNone, json.TokObject|json.TokArray)
 }
 
 // GeoJSON matches a RFC 7946 GeoJSON file.
 //
 // GeoJSON detection implies searching for key:value pairs like: `"type": "Feature"`
 // in the input.
-// BUG(gabriel-vasile): The "type" key should be searched for in the root object.
 func GeoJSON(raw []byte, limit uint32) bool {
-	raw = trimLWS(raw)
-	if len(raw) == 0 {
-		return false
-	}
-	// GeoJSON is always a JSON object, not a JSON array or any other JSON value.
-	if raw[0] != '{' {
-		return false
-	}
-
-	s := []byte(`"type"`)
-	si, sl := bytes.Index(raw, s), len(s)
+	return jsonHelper(raw, limit, json.QueryGeoJSON, json.TokObject)
+}
 
-	if si == -1 {
-		return false
-	}
+// HAR matches a HAR Spec file.
+// Spec: http://www.softwareishard.com/blog/har-12-spec/
+func HAR(raw []byte, limit uint32) bool {
+	return jsonHelper(raw, limit, json.QueryHARJSON, json.TokObject)
+}
 
-	// If the "type" string is the suffix of the input,
-	// there is no need to search for the value of the key.
-	if si+sl == len(raw) {
+func jsonHelper(raw []byte, limit uint32, q string, wantTok int) bool {
+	if !json.LooksLikeObjectOrArray(raw) {
 		return false
 	}
-	// Skip the "type" part.
-	raw = raw[si+sl:]
-	// Skip any whitespace before the colon.
-	raw = trimLWS(raw)
-	// Check for colon.
-	if len(raw) == 0 || raw[0] != ':' {
+	lraw := len(raw)
+	parsed, inspected, firstToken, querySatisfied := json.Parse(q, raw)
+	if !querySatisfied || firstToken&wantTok == 0 {
 		return false
 	}
-	// Skip any whitespace after the colon.
-	raw = trimLWS(raw[1:])
 
-	geoJSONTypes := [][]byte{
-		[]byte(`"Feature"`),
-		[]byte(`"FeatureCollection"`),
-		[]byte(`"Point"`),
-		[]byte(`"LineString"`),
-		[]byte(`"Polygon"`),
-		[]byte(`"MultiPoint"`),
-		[]byte(`"MultiLineString"`),
-		[]byte(`"MultiPolygon"`),
-		[]byte(`"GeometryCollection"`),
-	}
-	for _, t := range geoJSONTypes {
-		if bytes.HasPrefix(raw, t) {
-			return true
-		}
+	// If the full file content was provided, check that the whole input was parsed.
+	if limit == 0 || lraw < int(limit) {
+		return parsed == lraw
 	}
 
-	return false
+	// If a section of the file was provided, check if all of it was inspected.
+	// In other words, check that if there was a problem parsing, that problem
+	// occured at the last byte in the input.
+	return inspected == lraw && lraw > 0
 }
 
 // NdJSON matches a Newline delimited JSON file. All complete lines from raw
 // must be valid JSON documents meaning they contain one of the valid JSON data
 // types.
 func NdJSON(raw []byte, limit uint32) bool {
-	lCount, hasObjOrArr := 0, false
+	lCount, objOrArr := 0, 0
 	raw = dropLastLine(raw, limit)
 	var l []byte
 	for len(raw) != 0 {
 		l, raw = scanLine(raw)
-		// Empty lines are allowed in NDJSON.
-		if l = trimRWS(trimLWS(l)); len(l) == 0 {
-			continue
-		}
-		_, err := json.Scan(l)
-		if err != nil {
+		_, inspected, firstToken, _ := json.Parse(json.QueryNone, l)
+		if len(l) != inspected {
 			return false
 		}
-		if l[0] == '[' || l[0] == '{' {
-			hasObjOrArr = true
+		if firstToken == json.TokArray || firstToken == json.TokObject {
+			objOrArr++
 		}
 		lCount++
 	}
 
-	return lCount > 1 && hasObjOrArr
-}
-
-// HAR matches a HAR Spec file.
-// Spec: http://www.softwareishard.com/blog/har-12-spec/
-func HAR(raw []byte, limit uint32) bool {
-	s := []byte(`"log"`)
-	si, sl := bytes.Index(raw, s), len(s)
-
-	if si == -1 {
-		return false
-	}
-
-	// If the "log" string is the suffix of the input,
-	// there is no need to search for the value of the key.
-	if si+sl == len(raw) {
-		return false
-	}
-	// Skip the "log" part.
-	raw = raw[si+sl:]
-	// Skip any whitespace before the colon.
-	raw = trimLWS(raw)
-	// Check for colon.
-	if len(raw) == 0 || raw[0] != ':' {
-		return false
-	}
-	// Skip any whitespace after the colon.
-	raw = trimLWS(raw[1:])
-
-	harJSONTypes := [][]byte{
-		[]byte(`"version"`),
-		[]byte(`"creator"`),
-		[]byte(`"entries"`),
-	}
-	for _, t := range harJSONTypes {
-		si := bytes.Index(raw, t)
-		if si > -1 {
-			return true
-		}
-	}
-
-	return false
+	return lCount > 1 && objOrArr > 0
 }
 
 // Svg matches a SVG file.


Test patch:
diff --git a/internal/json/json_test.go b/internal/json/json_test.go
deleted file mode 100644
index 39ae2cb..0000000
--- a/internal/json/json_test.go
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright 2010 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 (
-	"strings"
-	"testing"
-)
-
-func TestScan(t *testing.T) {
-	tCases := []struct {
-		data   string
-		length int
-		ok     bool
-	}{
-		{`foo`, 2, false},
-		{`}{`, 1, false},
-		{`{]`, 2, false},
-		{`{}`, 2, true},
-		{`[]`, 2, true},
-		{`"foo"`, 5, true},
-		{`120`, 3, true},
-		{`1e20`, 4, true},
-		{`0.120`, 5, true},
-		{`null`, 4, true},
-		{`{"foo":"bar"}`, 13, true},
-		{`{"foo":"21\t\u0009 \u1234","bar":{"baz":["qux"]}`, 48, false},
-		{`{"foo":"bar","bar":{"baz":["qux"]}}`, 35, true},
-		{`{"foo":-1,"bar":{"baz":[true, false, null, 100, 0.123]}}`, 56, true},
-		{`{"foo":-1,"bar":{"baz":[tru]}}`, 28, false},
-		{`{"foo":-1,"bar":{"baz":[nul]}}`, 28, false},
-		{`{"foo":-1,"bar":{"baz":[314e+1]}}`, 33, true},
-	}
-	for _, st := range tCases {
-		scanned, err := Scan([]byte(st.data))
-		if scanned != st.length {
-			t.Errorf("Scan length error: expected: %d; got: %d; input: %s",
-				st.length, scanned, st.data)
-		}
-
-		if err != nil && st.ok {
-			t.Errorf("Scan failed with err: %s; input: %s", err, st.data)
-		}
-
-		if err == nil && !st.ok {
-			t.Errorf("Scan should fail for input: %s", st.data)
-		}
-	}
-}
-
-func TestScannerMaxDepth(t *testing.T) {
-	tCases := []struct {
-		name        string
-		data        string
-		errMaxDepth bool
-	}{
-		{
-			name:        "ArrayUnderMaxNestingDepth",
-			data:        `{"a":` + strings.Repeat(`[`, 10000-1) + strings.Repeat(`]`, 10000-1) + `}`,
-			errMaxDepth: false,
-		},
-		{
-			name:        "ArrayOverMaxNestingDepth",
-			data:        `{"a":` + strings.Repeat(`[`, 10000) + strings.Repeat(`]`, 10000) + `}`,
-			errMaxDepth: true,
-		},
-		{
-			name:        "ObjectUnderMaxNestingDepth",
-			data:        `{"a":` + strings.Repeat(`{"a":`, 10000-1) + `0` + strings.Repeat(`}`, 10000-1) + `}`,
-			errMaxDepth: false,
-		},
-		{
-			name:        "ObjectOverMaxNestingDepth",
-			data:        `{"a":` + strings.Repeat(`{"a":`, 10000) + `0` + strings.Repeat(`}`, 10000) + `}`,
-			errMaxDepth: true,
-		},
-	}
-
-	for _, tt := range tCases {
-		t.Run(tt.name, func(t *testing.T) {
-			_, err := Scan([]byte(tt.data))
-			if !tt.errMaxDepth {
-				if err != nil {
-					t.Errorf("unexpected error: %v", err)
-				}
-			} else {
-				if err == nil {
-					t.Errorf("expected error containing 'exceeded max depth', got none")
-				} else if !strings.Contains(err.Error(), "exceeded max depth") {
-					t.Errorf("expected error containing 'exceeded max depth', got: %v", err)
-				}
-			}
-		})
-	}
-}
diff --git a/internal/json/parser_test.go b/internal/json/parser_test.go
new file mode 100644
index 0000000..8b31339
--- /dev/null
+++ b/internal/json/parser_test.go
@@ -0,0 +1,814 @@
+package json
+
+import (
+	"bytes"
+	"encoding/json"
+	"strings"
+	"testing"
+)
+
+var sample = []byte(` { "type": "Feature", "fruit": "Apple", "size": "Large", "color": "Red" } `)
+
+// These samples come from https://github.com/nst/JSONTestSuite.
+var positives = []struct {
+	json   string
+	stdlib bool
+}{
+	{`[[]   ]`, true},
+	{`[]`, true},
+	{`[""]`, true},
+	{`["a"]`, true},
+	{`[false]`, true},
+	{`[null, 1, "1", {}]`, true},
+	{`[null]`, true},
+	{`[1
+]`, true},
+	{` [1]`, true},
+	{`[1,null,null,null,2]`, true},
+	{`[2] `, true},
+	{`[0e+1]`, true},
+	{`[0e1]`, true},
+	{`[ 4]`, true},
+	{`[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]
+`, true},
+	{`[20e1]`, true},
+	{`[123e65]`, true},
+	{`[-0]`, true},
+	{`[-123]`, true},
+	{`[-1]`, true},
+	{`[-0]`, true},
+	{`[1E22]`, true},
+	{`[1E-2]`, true},
+	{`[1E+2]`, true},
+	{`[123e45]`, true},
+	{`[123.456e78]`, true},
+	{`[1e-2]`, true},
+	{`[1e+2]`, true},
+	{`[123]`, true},
+	{`[123.456789]`, true},
+	{`{"asd":"sdf"}`, true},
+	{`{"a":"b","a":"b"}`, true},
+	{`{"a":"b","a":"c"}`, true},
+	{`{}`, true},
+	{`{"":0}`, true},
+	{`{"foo\u0000bar": 42}`, true},
+	{`{ "min": -1.0e+28, "max": 1.0e+28 }`, true},
+	{`{"asd":"sdf", "dfg":"fgh"}`, true},
+	{`{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}`, true},
+	{`{"a":[]}`, true},
+	{`{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }`, true},
+	{`{
+"a": "b"
+}`, true},
+	{`["\u0060\u012a\u12AB"]`, true},
+	{`["\uD801\udc37"]`, true},
+	{`["\ud83d\ude39\ud83d\udc8d"]`, true},
+	{`["\"\\\/\b\f\n\r\t"]`, true},
+	{`["\\u0000"]`, true},
+	{`["\""]`, true},
+	{`["a/*b*/c/*d//e"]`, true},
+	{`["\\a"]`, true},
+	{`["\\n"]`, true},
+	{`["\u0012"]`, true},
+	{`["\uFFFF"]`, true},
+	{`["asd"]`, true},
+	{`[ "asd"]`, true},
+	{`["\uDBFF\uDFFF"]`, true},
+	{`["new\u00A0line"]`, true},
+	{`["􏿿"]`, true},
+	{`["￿"]`, true},
+	{`["\u0000"]`, true},
+	{`["\u002c"]`, true},
+	{`["π"]`, true},
+	{`["𛿿"]`, true},
+	{`["asd "]`, true},
+	{`" "`, true},
+	{`["\uD834\uDd1e"]`, true},
+	{`["\u0821"]`, true},
+	{`["\u0123"]`, true},
+	{`[" "]`, true},
+	{`[" "]`, true},
+	{`["new\u000Aline"]`, true},
+	{`["\u0061\u30af\u30EA\u30b9"]`, true},
+	{`[""]`, true},
+	{`["⍂㈴⍂"]`, true},
+	{`["\u005C"]`, true},
+	{`["\u0022"]`, true},
+	{`["\uA66D"]`, true},
+	{`["\uDBFF\uDFFE"]`, true},
+	{`["\uD83F\uDFFE"]`, true},
+	{`["\u200B"]`, true},
+	{`["\u2064"]`, true},
+	{`["\uFDD0"]`, true},
+	{`["\uFFFE"]`, true},
+	{`["€𝄞"]`, true},
+	{`["aa"]`, true},
+	{`false`, true},
+	{`42`, true},
+	{`-0.1`, true},
+	{`null`, true},
+	{`"asd"`, true},
+	{`true`, true},
+	{`""`, true},
+	{`["a"]
+`, true},
+	{`[true]`, true},
+	{` [] `, true},
+
+	// Bug: following samples are invalid JSONs but they are parsed successfully.
+	{`    `, false},
+	{`["",]`, false},
+	{`[1,]`, false},
+	{`[-01]`, false},
+	{`[-2.]`, false},
+	{`[.2e-3]`, false},
+	{`[0.e1]`, false},
+	{`[2.e+3]`, false},
+	{`[2.e-3]`, false},
+	{`[2.e3]`, false},
+	{`[-012]`, false},
+	{`[-.123]`, false},
+	{`[1.]`, false},
+	{`[.123]`, false},
+	{`[012]`, false},
+	{`{"�":"0",}`, false},
+	{`{"id":0,}`, false},
+	{`"`, false},
+	{`["new
+line"]`, false},
+	{`["	"]`, false},
+	{`[`, false},
+	{`[[`, false},
+	{`{`, false},
+}
+
+var negatives = []struct {
+	name          string
+	json          string
+	expectParse   int
+	expectInspect int
+}{
+	{"array_1_true_without_comma", `[1 true]`, 1, 3},
+	{"array_a_invalid_utf8", `[a�]`, 1, 1},
+	{"array_colon_instead_of_comma", `["": 1]`, 1, 3},
+	{"array_comma_after_close", `[""],`, 4, 4},
+	{"array_comma_and_number", `[,1]`, 1, 1},
+	{"array_double_comma", `[1,,2]`, 1, 3},
+	{"array_double_extra_comma", `["x",,]`, 1, 5},
+	{"array_extra_close", `["x"]]`, 5, 5},
+	{"array_incomplete_invalid_value", `[x`, 1, 1},
+	{"array_incomplete", `["x"`, 1, 4},
+	{"array_inner_array_no_comma", `[3[4]]`, 1, 2},
+	{"array_invalid_utf8", `[�]`, 1, 1},
+	{"array_items_separated_by_semicolon", `[1:2]`, 1, 2},
+	{"array_just_comma", `[,]`, 1, 1},
+	{"array_just_minus", `[-]`, 1, 2},
+	{"array_missing_value", `[   , ""]`, 1, 4},
+	{"array_newlines_unclosed", `["a",
+4
+,1,`, 1, 11},
+	{"array_number_and_several_commas", `[1,,]`, 1, 3},
+	{"array_spaces_vertical_tab_formfeed", "\x5b\x22\x0b\x61\x22\x5c\x66\x5d", 1, 5},
+	{"array_star_inside", `[*]`, 1, 1},
+	{"array_unclosed", `[""`, 1, 3},
+	{"array_unclosed_trailing_comma", `[1,`, 1, 3},
+	{"array_unclosed_with_new_lines", "\x5b\x31\x2c\x0a\x31\x0a\x2c\x31", 1, 8},
+	{"array_unclosed_with_object_inside", `[{}`, 1, 3},
+	{"incomplete_false", `[fals]`, 1, 5},
+	{"incomplete_null", `[nul]`, 1, 4},
+	{"incomplete_true", `[tru]`, 1, 4},
+	{"multidigit_number_then_00", "\x31\x32\x33\x00", 3, 3},
+	{"number_0.1.2", `[0.1.2]`, 1, 4},
+	{"number_0.3e+", `[0.3e+]`, 1, 6},
+	{"number_0.3e", `[0.3e]`, 1, 5},
+	{"number_0_capital_E+", `[0E+]`, 1, 4},
+	{"number_0_capital_E", `[0E]`, 1, 3},
+	{"number_0e+", `[0e+]`, 1, 4},
+	{"number_0e", `[0e]`, 1, 3},
+	{"number_1_000", `[1 000.0]`, 1, 3},
+	{"number_1.0e+", `[1.0e+]`, 1, 6},
+	{"number_1.0e-", `[1.0e-]`, 1, 6},
+	{"number_1.0e", `[1.0e]`, 1, 5},
+	{"number_-1.0.", `[-1.0.]`, 1, 5},
+	{"number_1eE2", `[1eE2]`, 1, 3},
+	{"number_+1", `[+1]`, 1, 1},
+	{"number_.-1", `[.-1]`, 1, 2},
+	{"number_9.e+", `[9.e+]`, 1, 5},
+	{"number_expression", `[1+2]`, 1, 2},
+	{"number_hex_1_digit", `[0x1]`, 1, 2},
+	{"number_hex_2_digits", `[0x42]`, 1, 2},
+	{"number_infinity", `[Infinity]`, 1, 1},
+	{"number_+Inf", `[+Inf]`, 1, 1},
+	{"number_Inf", `[Inf]`, 1, 1},
+	{"number_invalid+-", `[0e+-1]`, 1, 4},
+	{"number_invalid-negative-real", `[-123.123foo]`, 1, 9},
+	{"number_invalid-utf-8-in-bigger-int", `[123�]`, 1, 4},
+	{"number_invalid-utf-8-in-exponent", `[1e1�]`, 1, 4},
+	{"number_invalid-utf-8-in-int", "\x5b\x30\xe5\x5d\x0a", 1, 2},
+	{"number_++", `[++1234]`, 1, 1},
+	{"number_minus_infinity", `[-Infinity]`, 1, 2},
+	{"number_minus_sign_with_trailing_garbage", `[-foo]`, 1, 2},
+	{"number_minus_space_1", `[- 1]`, 1, 2},
+	{"number_-NaN", `[-NaN]`, 1, 2},
+	{"number_NaN", `[NaN]`, 1, 1},
+	{"number_neg_with_garbage_at_end", `[-1x]`, 1, 3},
+	{"number_real_garbage_after_e", `[1ea]`, 1, 3},
+	{"number_real_with_invalid_utf8_after_e", `[1e�]`, 1, 3},
+	{"number_U+FF11_fullwidth_digit_one", `[１]`, 1, 1},
+	{"number_with_alpha_char", `[1.8011670033376514H-308]`, 1, 19},
+	{"number_with_alpha", `[1.2a-3]`, 1, 4},
+	{"object_bad_value", `["x", truth]`, 1, 9},
+	{"object_bracket_key", "\x7b\x5b\x3a\x20\x22\x78\x22\x7d\x0a", 1, 1},
+	{"object_comma_instead_of_colon", `{"x", null}`, 1, 4},
+	{"object_double_colon", `{"x"::"b"}`, 1, 5},
+	{"object_emoji", `{🇨🇭}`, 1, 1},
+	{"object_garbage_at_end", `{"a":"a" 123}`, 1, 9},
+	{"object_key_with_single_quotes", `{key: 'value'}`, 1, 1},
+	{"object_missing_colon", `{"a" b}`, 1, 5},
+	{"object_missing_key", `{:"b"}`, 1, 1},
+	{"object_missing_semicolon", `{"a" "b"}`, 1, 5},
+	{"object_missing_value", `{"a":`, 1, 5},
+	{"object_no-colon", `{"a"`, 1, 4},
+	{"object_non_string_key_but_huge_number_instead", `{9999E9999:1}`, 1, 1},
+	{"object_non_string_key", `{1:1}`, 1, 1},
+	{"object_repeated_null_null", `{null:null,null:null}`, 1, 1},
+	{"object_several_trailing_commas", `{"id":0,,,,,}`, 1, 8},
+	{"object_single_quote", `{'a':0}`, 1, 1},
+	{"object_trailing_comment", `{"a":"b"}/**/`, 9, 9},
+	{"object_trailing_comment_open", `{"a":"b"}/**//`, 9, 9},
+	{"object_trailing_comment_slash_open_incomplete", `{"a":"b"}/`, 9, 9},
+	{"object_trailing_comment_slash_open", `{"a":"b"}//`, 9, 9},
+	{"object_two_commas_in_a_row", `{"a":"b",,"c":"d"}`, 1, 9},
+	{"object_unquoted_key", `{a: "b"}`, 1, 1},
+	{"object_unterminated-value", `{"a":"a`, 1, 7},
+	{"object_with_single_string", `{ "foo" : "bar", "a" }`, 1, 21},
+	{"object_with_trailing_garbage", `{"a":"b"}#`, 9, 9},
+	{"single_space", ` `, 0, 1},
+	{"string_1_surrogate_then_escape", `["\uD800\"]`, 1, 11},
+	{"string_1_surrogate_then_escape_u1", `["\uD800\u1"]`, 1, 11},
+	{"string_1_surrogate_then_escape_u1x", `["\uD800\u1x"]`, 1, 11},
+	{"string_1_surrogate_then_escape_u", `["\uD800\u"]`, 1, 10},
+	{"string_accentuated_char_no_quotes", `[é]`, 1, 1},
+	{"string_backslash_00", "\x5b\x22\x5c\x00\x22\x5d", 1, 3},
+	{"string_escaped_backslash_bad", `["\\\"]`, 1, 7},
+	{"string_escaped_ctrl_char_tab", "\x5b\x22\x5c\x09\x22\x5d", 1, 3},
+	{"string_escaped_emoji", `["\🌀"]`, 1, 3},
+	{"string_escape_x", `["\x00"]`, 1, 3},
+	{"string_incomplete_escaped_character", `["\u00A"]`, 1, 7},
+	{"string_incomplete_escape", `["\"]`, 1, 5},
+	{"string_incomplete_surrogate_escape_invalid", `["\uD800\uD800\x"]`, 1, 15},
+	{"string_incomplete_surrogate", `["\uD834\uDd"]`, 1, 12},
+	{"string_invalid_backslash_esc", `["\a"]`, 1, 3},
+	{"string_invalid_unicode_escape", `["\uqqqq"]`, 1, 4},
+	{"string_invalid_utf8_after_escape", `["\�"]`, 1, 3},
+	{"string_invalid-utf-8-in-escape", `["\u�"]`, 1, 4},
+	{"string_leading_uescaped_thinspace", `[\u0020"asd"]`, 1, 1},
+	{"string_no_quotes_with_bad_escape", `[\n]`, 1, 1},
+	{"string_single_quote", `['single quote']`, 1, 1},
+	{"string_single_string_no_double_quotes", `abc`, 0, 0},
+	{"string_start_escape_unclosed", `["\`, 1, 3},
+	{"string_unicode_CapitalU", `"\UA66D"`, 1, 2},
+	{"string_with_trailing_garbage", `""x`, 2, 2},
+	{"structure_angle_bracket_.", `<.>`, 0, 0},
+	{"structure_angle_bracket_null", `[<null>]`, 1, 1},
+	{"structure_array_trailing_garbage", `[1]x`, 3, 3},
+	{"structure_array_with_extra_array_close", `[1]]`, 3, 3},
+	{"structure_array_with_unclosed_string", `["asd]`, 1, 6},
+	{"structure_ascii-unicode-identifier", `aå`, 0, 0},
+	{"structure_capitalized_True", `[True]`, 1, 1},
+	{"structure_close_unopened_array", `1]`, 1, 1},
+	{"structure_comma_instead_of_closing_brace", `{"x": true,`, 1, 11},
+	{"structure_double_array", `[][]`, 2, 2},
+	{"structure_end_array", `]`, 0, 0},
+	{"structure_incomplete_UTF8_BOM", `�{}`, 0, 0},
+	{"structure_lone-invalid-utf-8", `�`, 0, 0},
+	{"structure_null-byte-outside-string", "\x5b\x00\x5d", 1, 1},
+	{"structure_number_with_trailing_garbage", `2@`, 1, 1},
+	{"structure_object_followed_by_closing_object", `{}}`, 2, 2},
+	{"structure_object_unclosed_no_value", `{"":`, 1, 4},
+	{"structure_object_with_comment", `{"a":/*comment*/"b"}`, 1, 5},
+	{"structure_object_with_trailing_garbage", `{"a": true} "x"`, 12, 12},
+	{"structure_open_array_apostrophe", `['`, 1, 1},
+	{"structure_open_array_comma", `[,`, 1, 1},
+	{"structure_open_array_open_object", `[{`, 1, 2},
+	{"structure_open_array_open_string", `["a`, 1, 3},
+	{"structure_open_array_string", `["a"`, 1, 4},
+	{"structure_open_object_close_array", `{]`, 1, 1},
+	{"structure_open_object_comma", `{,`, 1, 1},
+	{"structure_open_object_open_array", `{[`, 1, 1},
+	{"structure_open_object_open_string", `{"a`, 1, 3},
+	{"structure_open_object_string_with_apostrophes", `{'a'`, 1, 1},
+	{"structure_open_open", `["\{["\{["\{["\{`, 1, 3},
+	{"structure_single_eacute", `�`, 0, 0},
+	{"structure_single_star", `*`, 0, 0},
+	{"structure_trailing_#", `{"a":"b"}#{}`, 9, 9},
+	{"structure_U+2060_word_joined", "\x5b\xe2\x81\xa0\x5d", 1, 1},
+	{"structure_uescaped_LF_before_string", `[\u000A""]`, 1, 1},
+	{"structure_unclosed_array", `[1`, 1, 2},
+	{"structure_unclosed_array_partial_null", `[ false, nul`, 1, 12},
+	{"structure_unclosed_array_unfinished_false", `[ true, fals`, 1, 12},
+	{"structure_unclosed_array_unfinished_true", `[ false, tru`, 1, 12},
+	{"structure_unclosed_object", `{"asd":"asd"`, 1, 12},
+	{"structure_unicode-identifier", `å`, 0, 0},
+	{"structure_UTF8_BOM_no_data", "\xef\xbb\xbf", 0, 0},
+	{"structure_whitespace_formfeed", "\x5b\x0c\x5d", 1, 1},
+	{"structure_whitespace_U+2060_word_joiner", "\x5b\xe2\x81\xa0\x5d", 1, 1},
+}
+
+func TestConsumeString(t *testing.T) {
+	tCases := []struct {
+		name     string
+		data     string
+		expected int
+	}{
+		{"ascii string", `foo"`, 4},
+		{"utf-8 string one char", `ß"`, 3},
+		{"utf-8 string multiple chars", `ßßßß"`, 9},
+		{"empty string", ``, 0},
+		{"non-ending ascii string", `a`, 0},
+		{"non-ending utf-8 string", `ß`, 0},
+		{"escaped ascii string", "\\b a\"", 5},
+		{"escaped utf-8 string", "\\b ß\"", 6},
+	}
+
+	for _, tt := range tCases {
+		t.Run(tt.name, func(t *testing.T) {
+			p := &parserState{}
+			got := p.consumeString([]byte(tt.data))
+			if got != tt.expected {
+				t.Errorf("expected: %v, got: %v", tt.expected, got)
+			}
+		})
+	}
+}
+
+func TestConsumeNumber(t *testing.T) {
+	tCases := []struct {
+		data     string
+		expected int
+	}{
+		{`123`, 3},
+		{`123.1`, 5},
+		{`123.`, 4},
+		{`.123`, 4},
+		{`.`, 0},
+		{`..`, 0},
+		{`e`, 0},
+		{`1e1`, 3},
+		{`1.1e1`, 5},
+		{`.1e1`, 4},
+		{"", 0},
+		{`"NaN"`, 0},
+		{`"Infinity"`, 0},
+		{`"-Infinity"`, 0},
+		{".0", 2},
+		{"0", 1},
+		{"-0", 2},
+		{"+0", 0},
+		{"1", 1},
+		{"-1", 2},
+		{"00", 2},
+		{"-00", 3},
+		{"01", 2},
+		{"-01", 3},
+		{"0i", 1},
+		{"-0i", 2},
+		{"0f", 1},
+		{"-0f", 2},
+		{"9876543210", 10},
+		{"-9876543210", 11},
+		{"9876543210x", 10},
+		{"-9876543210x", 11},
+		{" 9876543210", 0},
+		{"- 9876543210", 0},
+		{strings.Repeat("9876543210", 1000), 10000},
+		{"-" + strings.Repeat("9876543210", 1000), 1 + 10000},
+		{"0.", 2},
+		{"-0.", 3},
+		{"0e", 0},
+		{"-0e", 0},
+		{"0E", 0},
+		{"-0E", 0},
+		{"0.0", 3},
+		{"-0.0", 4},
+		{"0e0", 3},
+		{"-0e0", 4},
+		{"0E0", 3},
+		{"-0E0", 4},
+		{"0.0123456789", 12},
+		{"-0.0123456789", 13},
+		{"1.f", 2},
+		{"-1.f", 3},
+		{"1.e", 0},
+		{"-1.e", 0},
+		{"1e0", 3},
+		{"-1e0", 4},
+		{"1E0", 3},
+		{"-1E0", 4},
+		{"1Ex", 0},
+		{"-1Ex", 0},
+		{"1e-0", 4},
+		{"-1e-0", 5},
+		{"1e+0", 4},
+		{"-1e+0", 5},
+		{"1E-0", 4},
+		{"-1E-0", 5},
+		{"1E+0", 4},
+		{"-1E+0", 5},
+		{"1E+00500", 8},
+		{"-1E+00500", 9},
+		{"1E+00500x", 8},
+		{"-1E+00500x", 9},
+		{"9876543210.0123456789e+01234589x", 31},
+		{"-9876543210.0123456789e+01234589x", 32},
+		{"1_000_000", 1},
+		{"0x12ef", 1},
+		{"0x1p-2", 1},
+	}
+
+	p := &parserState{}
+	for _, tt := range tCases {
+		tname := tt.data
+		if len(tname) > 10 {
+			tname = tname[:10] + "..."
+		}
+		t.Run(tname, func(t *testing.T) {
+			got := p.consumeNumber([]byte(tt.data))
+			if got != tt.expected {
+				t.Errorf("expected: %v, got: %v", tt.expected, got)
+			}
+		})
+	}
+}
+
+func TestConsumeArray(t *testing.T) {
+	tCases := []struct {
+		name     string
+		data     string
+		expected int
+	}{
+		{"empty array", `]`, 1},
+		{"empty array spaces", ` ]`, 2},
+		{"one int array", `1]`, 2},
+		{"one int array spaces", ` 1 ]`, 4},
+		{"two ints array", `1,2]`, 4},
+		{"two ints array spaces", ` 1 , 2 ]`, 8},
+		{"everything array", `[], {}, true, false, null, 1, "abc"]`, 36},
+		{"everything array v2", `[1,2,3], {"a":"b"}, true, false, null, 1, "abc"]`, 48},
+		{"escaped \"", `"\""]`, 5},
+		{"hex", `"\uA66D"]`, 9},
+		{"unfinished string", `"\uFFF`, 0},
+	}
+
+	p := &parserState{}
+	for _, tt := range tCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := p.consumeArray([]byte(tt.data), nil, 1)
+			if got != tt.expected {
+				t.Errorf("expected: %v, got: %v", tt.expected, got)
+			}
+		})
+	}
+}
+
+func TestQueryObject(t *testing.T) {
+	tCases := []struct {
+		json         string
+		query        query
+		expectedFind bool
+	}{{
+		json: `{"foo": {"bar": "baz"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("")},
+		},
+		expectedFind: false,
+	}, {
+		json: `{"foo": {"bar": "baz"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("fool")},
+		},
+		expectedFind: false,
+	}, {
+		json: `{"foo": {"bar": "baz"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("afoo")},
+		},
+		expectedFind: false,
+	}, {
+		json: `{"foo": {"bar": "baz"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte(""), []byte("foo")},
+		},
+		expectedFind: false,
+	}, {
+		json: `{"foo": {"bar": "baz"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("bar"), []byte("foo")},
+		},
+		expectedFind: false,
+	}, {
+		json: `{"foo": {"bar": "foo"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("bar"), []byte("foo")},
+		},
+		expectedFind: false,
+	}, {
+		json: `[{"foo": {"bar": "baz"}}]`,
+		query: query{
+			SearchPath: [][]byte{[]byte("foo"), []byte("bar")},
+		},
+		expectedFind: false,
+	}, {
+		json: `{"foo": {"bar": "baz"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("foo"), []byte("bar")},
+		},
+		expectedFind: true,
+	}, {
+		json: `{"foo": {"bar": "baz"}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("foo"), []byte("bar")},
+			SearchVals: [][]byte{[]byte(`"baz"`)},
+		},
+		expectedFind: true,
+	}, {
+		json: `{"foo": {"foo": {"bar": "baz"}}}`,
+		query: query{
+			SearchPath: [][]byte{[]byte("foo"), []byte("bar")},
+			SearchVals: [][]byte{[]byte(`"baz"`)},
+		},
+		expectedFind: false,
+	}}
+
+	for _, tt := range tCases {
+		t.Run(tt.json, func(t *testing.T) {
+			p := &parserState{}
+			p.consumeAny([]byte(tt.json), []query{tt.query}, 0)
+			if tt.expectedFind != p.querySatisfied {
+				t.Errorf("expectedFind: %v, got: %v", tt.expectedFind, p.querySatisfied)
+			}
+		})
+	}
+}
+
+func TestConsumeObject(t *testing.T) {
+	tCases := []struct {
+		name     string
+		data     string
+		expected int
+	}{
+		{"empty object", `}`, 1},
+		{"object", `"a":"b"}`, 8},
+		{"panic found with fuzz", "\"\":0", 0},
+	}
+
+	p := &parserState{}
+	for _, tt := range tCases {
+		t.Run(tt.name, func(t *testing.T) {
+			got := p.consumeObject([]byte(tt.data), nil, 1)
+			if got != tt.expected {
+				t.Errorf("expected: %v, got: %v", tt.expected, got)
+			}
+		})
+	}
+}
+
+func TestConsumeConst(t *testing.T) {
+	tCases := []struct {
+		b       string
+		cnst    string
+		expect  int
+		inspect int
+	}{
+		{"", "", 0, 0},
+		{"", "true", 0, 0},
+		{"true", "", 0, 0},
+		{"t", "true", 0, 1},
+		{"tr", "true", 0, 2},
+		{"tru", "true", 0, 3},
+		{"true", "true", 4, 4},
+		{"truex", "true", 4, 4},
+	}
+
+	for _, tt := range tCases {
+		p := &parserState{}
+		t.Run(tt.b+" -- "+tt.cnst, func(t *testing.T) {
+			got := p.consumeConst([]byte(tt.b), []byte(tt.cnst))
+			if got != tt.expect {
+				t.Errorf("expected: %v, got %v", tt.expect, got)
+			}
+			if p.ib != tt.inspect {
+				t.Errorf("expected to inspect: %v, got %v", tt.inspect, p.ib)
+			}
+		})
+	}
+}
+
+// Truncate inputs at each possible index and test if decoder parses
+// the truncated part successfully.
+func testTruncating(t *testing.T, jsonString string) {
+	t.Helper()
+	p := &parserState{}
+	for i := 1; i <= len(jsonString); i++ {
+		trunc := trimRWS([]byte(jsonString)[:i])
+		p.reset()
+		_ = p.consumeAny(trunc, nil, 0)
+		if p.ib != len(trunc) {
+			t.Errorf("truncated positives should be fully parsed %v \n"+
+				"got: %d want: %d", string(trunc), p.ib, len(trunc))
+		}
+	}
+}
+
+func TestPositives(t *testing.T) {
+	for _, tt := range positives {
+		testTruncating(t, tt.json)
+	}
+}
+
+func TestPositivesCompacted(t *testing.T) {
+	for _, tt := range positives {
+		if !tt.stdlib {
+			continue
+		}
+		buf := &bytes.Buffer{}
+		if err := json.Compact(buf, []byte(tt.json)); err != nil {
+			t.Errorf("Compact should always be successful: %s %s", tt.json, err)
+		}
+		testTruncating(t, buf.String())
+	}
+}
+
+func TestPositivesIndented(t *testing.T) {
+	indents := [][2]string{
+		{"", " "},
+		{" ", " "},
+		{" ", "\t"},
+		{"\t", "\t"},
+		{"\t", " \t"},
+		{"", "\r\n"},
+		{"", " \r\n"},
+	}
+	for _, tt := range positives {
+		if !tt.stdlib {
+			continue
+		}
+		for _, indent := range indents {
+			buf := &bytes.Buffer{}
+			if err := json.Indent(buf, []byte(tt.json), indent[0], indent[1]); err != nil {
+				t.Errorf("Indent should always be successful: %s %s", tt.json, err)
+			}
+			testTruncating(t, buf.String())
+		}
+	}
+}
+
+func TestNegatives(t *testing.T) {
+	p := &parserState{}
+	for _, tt := range negatives {
+		t.Run(tt.name, func(t *testing.T) {
+			p.reset()
+			got := p.consumeAny([]byte(tt.json), nil, 0)
+			if got != tt.expectParse {
+				t.Errorf("unexpected parsed length got: %d want:%d", got, tt.expectParse)
+			}
+			if p.ib != tt.expectInspect {
+				t.Errorf("unexpected inspected length got: %d want:%d\nin:%s", p.ib, tt.expectInspect, tt.json)
+			}
+		})
+	}
+}
+
+func TestMaxRecursion(t *testing.T) {
+	tCases := []struct {
+		maxRecursion    int
+		input           string
+		expectParsed    int
+		expectInspected int
+	}{
+		{0, `[]`, 2, 2},
+		{0, `[[[]]]`, 6, 6},
+		{0, strings.Repeat("[", 10000) + strings.Repeat("]", 10000), 20000, 20000},
+		{3, `[[[[[]]]]]`, 1, 4}, // max recursion is 3 so we need to inspect 4 opening brackets
+	}
+	for _, tt := range tCases {
+		tname := tt.input
+		if len(tname) > 10 {
+			tname = tname[:10] + "..."
+		}
+		t.Run(tname, func(t *testing.T) {
+			p := &parserState{
+				maxRecursion: tt.maxRecursion,
+			}
+			got := p.consumeAny([]byte(tt.input), nil, 0)
+			if got != tt.expectParsed {
+				t.Errorf("parsed: got: %d expected: %d", got, tt.expectParsed)
+			}
+			if p.ib != tt.expectInspected {
+				t.Errorf("inspected: got: %d expected: %d", p.ib, tt.expectInspected)
+			}
+		})
+	}
+}
+
+func TestStack(t *testing.T) {
+	tCases := []struct {
+		name     string
+		data     string
+		expected string
+	}{
+		{"empty", ` `, ""},
+		{"a string", `"abc"`, ""},
+		{"an int", `123`, ""},
+		{"true", `true`, ""},
+		{"false", `false`, ""},
+		// Input must be an incomplete JSON because the stack is popped otherwise.
+		{"arr", `[`, "["},
+		// Put a § between each segment of the stack.
+		{"arrr", `[[`, "[§["},
+		{"arrrr", `[[[`, "[§[§["},
+		{"arrr popped once", `[[[]`, "[§["},
+		{"obj", `{`, ""},
+		{"obj key", `{"abc":1`, "abc"},
+		{"obj key twice", `{"abc":{"def":1`, "abc§def"},
+		{"obj key twice but popped", `{"abc":{"def":1}`, "abc"},
+		{"obj key twice and arr", `{"abc":{"def":[`, "abc§def§["},
+		{"hacky", `{"abc":{"def[":`, "abc§def["},
+	}
+
+	join := func(bs [][]byte) string {
+		ret := []string{}
+		for _, b := range bs {
+			ret = append(ret, string(b))
+		}
+		return strings.Join(ret, "§")
+	}
+	for _, tt := range tCases {
+		t.Run(tt.name, func(t *testing.T) {
+			p := &parserState{}
+			p.consumeAny([]byte(tt.data), nil, 0)
+			if got := join(p.currPath); got != tt.expected {
+				t.Errorf("expected: %s, got: %s", tt.expected, got)
+			}
+		})
+	}
+}
+
+func BenchmarkParse(b *testing.B) {
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		_, _, _, query := Parse(QueryGeoJSON, sample)
+		if !query {
+			b.Error("query should be satisfied")
+		}
+	}
+}
+
+func BenchmarkJSONStdlibDecoder(b *testing.B) {
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		d := json.NewDecoder(bytes.NewReader(sample))
+		for {
+			_, err := d.Token()
+			if err != nil {
+				break
+			}
+		}
+	}
+}
+func BenchmarkJSONOurParser(b *testing.B) {
+	b.ReportAllocs()
+	for i := 0; i < b.N; i++ {
+		p := &parserState{}
+		p.consumeAny(sample, nil, 0)
+	}
+}
+
+func FuzzJson(f *testing.F) {
+	for _, p := range positives {
+		f.Add([]byte(p.json), true)
+	}
+	p := &parserState{}
+	f.Fuzz(func(t *testing.T, data []byte, reset bool) {
+		if reset {
+			p.reset()
+		}
+		p.consumeString(data)
+		p.consumeNumber(data)
+		p.consumeArray(data, nil, 1)
+		p.consumeObject(data, nil, 1)
+		p.consumeAny(data, nil, 1)
+	})
+}
+
+// trimRWS trims whitespace from the end of the input.
+func trimRWS(in []byte) []byte {
+	lastNonWS := len(in) - 1
+	for ; lastNonWS > 0 && isWS(in[lastNonWS]); lastNonWS-- {
+	}
+
+	return in[:lastNonWS+1]
+}
+func isWS(b byte) bool {
+	return b == '\t' || b == '\n' || b == '\x0c' || b == '\r' || b == ' '
+}
diff --git a/internal/magic/magic_test.go b/internal/magic/magic_test.go
index 4f6da3b..194edc3 100644
--- a/internal/magic/magic_test.go
+++ b/internal/magic/magic_test.go
@@ -210,6 +210,9 @@ func TestDropLastLine(t *testing.T) {
 		{"\na\n", 3, "\na"},
 		{"å\n\n", 5, "å\n\n"},
 		{"\nå\n", 5, "\nå\n"},
+		// JSON with new-lines inside string value.
+		{"{\"a\" : \"b\\n\\n\"}", 5, "{\"a\" : \"b\\n\\n\"}"},
+		{"{\"a\" : \"b\\n\\n\"}", 100, "{\"a\" : \"b\\n\\n\"}"},
 	}
 	for i, tt := range dropTests {
 		got := dropLastLine([]byte(tt.raw), tt.cutAt)
diff --git a/mimetype_test.go b/mimetype_test.go
index ece446a..865b0aa 100644
--- a/mimetype_test.go
+++ b/mimetype_test.go
@@ -97,6 +97,7 @@ var testcases = []testcase{
 	{"flv", "\x46\x4C\x56\x01", "video/x-flv", true},
 	{"gbr", offset(20, "GIMP"), "image/x-gimp-gbr", true},
 	{"geojson", `{"type":"Feature"}`, "application/geo+json", true},
+	{"geojson with space", `{ "type" : "Feature" }`, "application/geo+json", false},
 	{"gif 87", "GIF87a", "image/gif", true},
 	{"gif 89", "GIF89a", "image/gif", false},
 	{"glb 1", "\x67\x6C\x54\x46\x02\x00\x00\x00", "model/gltf-binary", true},
@@ -140,6 +141,7 @@ var testcases = []testcase{
 	{"xpm", "\x2F\x2A\x20\x58\x50\x4D\x20\x2A\x2F", "image/x-xpixmap", true},
 	{"js", "#!/bin/node ", "text/javascript", true},
 	{"json", `{"key":"val"}`, "application/json", true},
+	{"json array", `[1,2,3]`, "application/json", false},
 	{"json issue#239", "{\x0A\x09\x09\"key\":\"val\"}\x0A", "application/json", false},
 	// json.{int,string}.txt contain a single JSON value. They are valid JSON
 	// documents but they should not be detected as application/json. This mimics
@@ -181,6 +183,7 @@ var testcases = []testcase{
 	{"msi", fromDisk("msi.msi"), "application/x-ms-installer", true},
 	{"msg", fromDisk("msg.msg"), "application/vnd.ms-outlook", true},
 	{"ndjson", `{"key":"val"}` + "\n" + `{"key":"val"}`, "application/x-ndjson", true},
+	{"ndjson spaces", `{ "key" : "val" }` + "\n" + ` { "key" : "val" }`, "application/x-ndjson", true},
 	{"nes", "NES\x1a", "application/vnd.nintendo.snes.rom", true},
 	{"elfobject", "\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00", "application/x-object", true},
 	{"odf", "PK\x03\x04\x14\x00\x00\x08\x00\x00\xb1Z\xa8N\x07\x8a\xa8[*\x00\x00\x00*\x00\x00\x00\x08\x00\x00\x00mimetypeapplication/vnd.oasis.opendocument.formula", "application/vnd.oasis.opendocument.formula", true},
@@ -495,7 +498,7 @@ func BenchmarkAll(b *testing.B) {
 		parsed, _, _ := mime.ParseMediaType(tc.expectedMIME)
 		mtype := Lookup(parsed)
 		if mtype == nil || mtype.detector == nil {
-			b.Fatalf("nu e bine %s %s", mtype, tc.expectedMIME)
+			b.Fatalf("mime should always be non-nil %s %s", mtype, tc.expectedMIME)
 		}
 		// data is used for the positive case benchmark.
 		b.Run(tc.name, func(b *testing.B) {