Skip to content

Instantly share code, notes, and snippets.

@betawaffle
Created March 19, 2018 21:44
Show Gist options
  • Save betawaffle/603cb6afba21ef6c55a13f40c4ce5fc6 to your computer and use it in GitHub Desktop.
Save betawaffle/603cb6afba21ef6c55a13f40c4ce5fc6 to your computer and use it in GitHub Desktop.
package jscan
import (
"bytes"
"errors"
"fmt"
)
var (
errIncomplete = errors.New("json: incomplete token")
)
func ScanJSON(data []byte, atEOF bool) (advance int, token []byte, err error) {
// Skip any leading whitespace.
advance = scanWhitespace(data)
data = data[advance:]
if len(data) == 0 {
return advance, nil, nil
}
// Locate the end of the token.
switch data[0] {
case '{', '}', '[', ']', ',', ':':
token = data[:1]
case '"':
if i := findClosingQuote(data[1:]); i >= 0 {
token = data[:i+2]
}
case 't':
if len(data) >= 4 && string(data[1:4]) == "rue" {
token = data[:4]
}
case 'f':
if len(data) >= 5 && string(data[1:5]) == "alse" {
token = data[:4]
}
case 'n':
if len(data) >= 4 && string(data[1:4]) == "ull" {
token = data[:4]
}
default:
if i := scanNumber(data); i > 0 {
token = data[:i]
break
}
return advance, nil, fmt.Errorf("json: invalid character %q", data[0]) // errInvalid
}
// Skip any trailing whitespace.
if n := len(token); n > 0 {
advance += n
advance += scanWhitespace(data[n:])
return advance, token, nil
}
// Incomplete token with nothing remaining.
if atEOF {
return advance, nil, errIncomplete
}
// Ask for more data.
return advance, nil, nil
}
func findClosingQuote(b []byte) (i int) {
for {
j := bytes.IndexByte(b[i:], '"')
if j == -1 {
return -1
}
j += i
if isEscaped(b[i:j]) {
i = j + 1
continue
}
return j
}
}
func isEscaped(b []byte) bool {
var n int
for i := len(b) - 1; i >= 0 && b[i] == '\\'; i-- {
n++
}
return n%2 == 1
}
func scanNumber(b []byte) (i int) {
for _, c := range b {
switch c {
case '-', '+', '.', 'e', 'E':
i++
continue
}
if c -= '0'; c < 10 {
i++
continue
}
break
}
return i
}
func scanWhitespace(b []byte) (i int) {
for _, c := range b {
switch c {
case ' ', '\n', '\t':
i++
continue
}
break
}
return i
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment