Merge pull request #221 from goccy/feature/refactor-validator

Refactor validator
This commit is contained in:
Masaaki Goshima 2021-05-09 14:44:12 +09:00 committed by GitHub
commit 835c00e18b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 107 additions and 261 deletions

View File

@ -39,10 +39,20 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: 1.16
- name: checkout
- name: checkout ( feature )
uses: actions/checkout@v2
- name: run benchmark
run: cd benchmarks && go test -bench .
- name: run benchmark ( feature )
run: cd benchmarks && go test -bench GoJson | tee $HOME/new.txt
- name: install benchcmp
run: go get -u golang.org/x/tools/cmd/benchcmp
- name: checkout ( master )
uses: actions/checkout@v2
with:
ref: master
- name: run benchmark ( master )
run: cd benchmarks && go test -bench GoJson | tee $HOME/old.txt
- name: compare benchmark results
run: benchcmp $HOME/old.txt $HOME/new.txt
coverage:
name: Coverage
runs-on: ubuntu-latest

View File

@ -95,24 +95,14 @@ func (d *arrayDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
return 0, errExceededMaxDepth(buf[cursor], cursor)
}
buflen := int64(len(buf))
for ; cursor < buflen; cursor++ {
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
continue
case 'n':
buflen := int64(len(buf))
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
cursor += 4
return cursor, nil
@ -149,7 +139,8 @@ func (d *arrayDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
return 0, errInvalidCharacter(buf[cursor], "array", cursor)
}
}
}
}
default:
return 0, errUnexpectedEndOfJSON("array", cursor)
}
}
}

View File

@ -47,56 +47,25 @@ ERROR:
}
func (d *boolDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
buflen := int64(len(buf))
cursor = skipWhiteSpace(buf, cursor)
switch buf[cursor] {
case 't':
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("bool(true)", cursor)
}
if buf[cursor+1] != 'r' {
return 0, errInvalidCharacter(buf[cursor+1], "bool(true)", cursor)
}
if buf[cursor+2] != 'u' {
return 0, errInvalidCharacter(buf[cursor+2], "bool(true)", cursor)
}
if buf[cursor+3] != 'e' {
return 0, errInvalidCharacter(buf[cursor+3], "bool(true)", cursor)
if err := validateTrue(buf, cursor); err != nil {
return 0, err
}
cursor += 4
**(**bool)(unsafe.Pointer(&p)) = true
return cursor, nil
case 'f':
if cursor+4 >= buflen {
return 0, errUnexpectedEndOfJSON("bool(false)", cursor)
}
if buf[cursor+1] != 'a' {
return 0, errInvalidCharacter(buf[cursor+1], "bool(false)", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "bool(false)", cursor)
}
if buf[cursor+3] != 's' {
return 0, errInvalidCharacter(buf[cursor+3], "bool(false)", cursor)
}
if buf[cursor+4] != 'e' {
return 0, errInvalidCharacter(buf[cursor+4], "bool(false)", cursor)
if err := validateFalse(buf, cursor); err != nil {
return 0, err
}
cursor += 5
**(**bool)(unsafe.Pointer(&p)) = false
return cursor, nil
case 'n':
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
cursor += 4
return cursor, nil

View File

@ -166,9 +166,7 @@ func (d *bytesDecoder) decodeBinary(buf []byte, cursor, depth int64, p unsafe.Po
cursor += 4
return nil, cursor, nil
default:
goto ERROR
}
}
ERROR:
return nil, 0, errNotAtBeginningOfValue(cursor)
}
}
}

View File

@ -153,37 +153,14 @@ func skipValue(buf []byte, cursor, depth int64) (int64, error) {
}
return cursor, nil
case 't':
buflen := int64(len(buf))
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
}
if buf[cursor+1] != 'r' {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
}
if buf[cursor+2] != 'u' {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
}
if buf[cursor+3] != 'e' {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
if err := validateTrue(buf, cursor); err != nil {
return 0, err
}
cursor += 4
return cursor, nil
case 'f':
buflen := int64(len(buf))
if cursor+4 >= buflen {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
}
if buf[cursor+1] != 'a' {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
}
if buf[cursor+3] != 's' {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
}
if buf[cursor+4] != 'e' {
return 0, errUnexpectedEndOfJSON("bool of object", cursor)
if err := validateFalse(buf, cursor); err != nil {
return 0, err
}
cursor += 5
return cursor, nil
@ -199,6 +176,41 @@ func skipValue(buf []byte, cursor, depth int64) (int64, error) {
}
}
func validateTrue(buf []byte, cursor int64) error {
if cursor+3 >= int64(len(buf)) {
return errUnexpectedEndOfJSON("true", cursor)
}
if buf[cursor+1] != 'r' {
return errInvalidCharacter(buf[cursor+1], "true", cursor)
}
if buf[cursor+2] != 'u' {
return errInvalidCharacter(buf[cursor+2], "true", cursor)
}
if buf[cursor+3] != 'e' {
return errInvalidCharacter(buf[cursor+3], "true", cursor)
}
return nil
}
func validateFalse(buf []byte, cursor int64) error {
if cursor+4 >= int64(len(buf)) {
return errUnexpectedEndOfJSON("false", cursor)
}
if buf[cursor+1] != 'a' {
return errInvalidCharacter(buf[cursor+1], "false", cursor)
}
if buf[cursor+2] != 'l' {
return errInvalidCharacter(buf[cursor+2], "false", cursor)
}
if buf[cursor+3] != 's' {
return errInvalidCharacter(buf[cursor+3], "false", cursor)
}
if buf[cursor+4] != 'e' {
return errInvalidCharacter(buf[cursor+4], "false", cursor)
}
return nil
}
func validateNull(buf []byte, cursor int64) error {
if cursor+3 >= int64(len(buf)) {
return errUnexpectedEndOfJSON("null", cursor)

View File

@ -91,34 +91,22 @@ ERROR:
}
func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
buflen := int64(len(buf))
for ; cursor < buflen; cursor++ {
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
continue
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := cursor
cursor++
for ; cursor < buflen; cursor++ {
if floatTable[buf[cursor]] {
continue
}
break
for floatTable[buf[cursor]] {
cursor++
}
num := buf[start:cursor]
return num, cursor, nil
case 'n':
if cursor+3 >= buflen {
return nil, 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return nil, 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return nil, cursor, nil
@ -126,7 +114,6 @@ func (d *floatDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, erro
return nil, 0, errUnexpectedEndOfJSON("float", cursor)
}
}
return nil, 0, errUnexpectedEndOfJSON("float", cursor)
}
func (d *floatDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {

View File

@ -154,26 +154,14 @@ func (d *intDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error)
case '-', '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := cursor
cursor++
LOOP:
if numTable[char(b, cursor)] {
for numTable[char(b, cursor)] {
cursor++
goto LOOP
}
num := buf[start:cursor]
return num, cursor, nil
case 'n':
buflen := int64(len(buf))
if cursor+3 >= buflen {
return nil, 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return nil, 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return nil, cursor, nil

View File

@ -385,52 +385,22 @@ func (d *interfaceDecoder) decodeEmptyInterface(buf []byte, cursor, depth int64,
**(**interface{})(unsafe.Pointer(&p)) = v
return cursor, nil
case 't':
if cursor+3 >= int64(len(buf)) {
return 0, errUnexpectedEndOfJSON("bool(true)", cursor)
}
if buf[cursor+1] != 'r' {
return 0, errInvalidCharacter(buf[cursor+1], "bool(true)", cursor)
}
if buf[cursor+2] != 'u' {
return 0, errInvalidCharacter(buf[cursor+2], "bool(true)", cursor)
}
if buf[cursor+3] != 'e' {
return 0, errInvalidCharacter(buf[cursor+3], "bool(true)", cursor)
if err := validateTrue(buf, cursor); err != nil {
return 0, err
}
cursor += 4
**(**interface{})(unsafe.Pointer(&p)) = true
return cursor, nil
case 'f':
if cursor+4 >= int64(len(buf)) {
return 0, errUnexpectedEndOfJSON("bool(false)", cursor)
}
if buf[cursor+1] != 'a' {
return 0, errInvalidCharacter(buf[cursor+1], "bool(false)", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "bool(false)", cursor)
}
if buf[cursor+3] != 's' {
return 0, errInvalidCharacter(buf[cursor+3], "bool(false)", cursor)
}
if buf[cursor+4] != 'e' {
return 0, errInvalidCharacter(buf[cursor+4], "bool(false)", cursor)
if err := validateFalse(buf, cursor); err != nil {
return 0, err
}
cursor += 5
**(**interface{})(unsafe.Pointer(&p)) = false
return cursor, nil
case 'n':
if cursor+3 >= int64(len(buf)) {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
cursor += 4
**(**interface{})(unsafe.Pointer(&p)) = nil

View File

@ -102,17 +102,8 @@ func (d *mapDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (
}
switch buf[cursor] {
case 'n':
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
cursor += 4
**(**unsafe.Pointer)(unsafe.Pointer(&p)) = nil

View File

@ -77,34 +77,22 @@ ERROR:
}
func (d *numberDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
buflen := int64(len(buf))
for ; cursor < buflen; cursor++ {
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
continue
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := cursor
cursor++
for ; cursor < buflen; cursor++ {
if floatTable[buf[cursor]] {
continue
}
break
for floatTable[buf[cursor]] {
cursor++
}
num := buf[start:cursor]
return num, cursor, nil
case 'n':
if cursor+3 >= buflen {
return nil, 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return nil, 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return nil, cursor, nil
@ -114,5 +102,4 @@ func (d *numberDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
return nil, 0, errUnexpectedEndOfJSON("json.Number", cursor)
}
}
return nil, 0, errUnexpectedEndOfJSON("json.Number", cursor)
}

View File

@ -60,18 +60,8 @@ func (d *ptrDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) erro
func (d *ptrDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer) (int64, error) {
cursor = skipWhiteSpace(buf, cursor)
if buf[cursor] == 'n' {
buflen := int64(len(buf))
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
if p != nil {
*(*unsafe.Pointer)(p) = nil

View File

@ -186,24 +186,14 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
return 0, errExceededMaxDepth(buf[cursor], cursor)
}
buflen := int64(len(buf))
for ; cursor < buflen; cursor++ {
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
continue
case 'n':
buflen := int64(len(buf))
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
cursor += 4
*(*unsafe.Pointer)(p) = nil
@ -274,9 +264,7 @@ func (d *sliceDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer)
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
return 0, d.errNumber(cursor)
default:
goto ERROR
}
}
ERROR:
return 0, errUnexpectedEndOfJSON("slice", cursor)
}
}
}

View File

@ -343,25 +343,13 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err
cursor++
}
case 'n':
buflen := int64(len(buf))
if cursor+3 >= buflen {
return nil, 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return nil, 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return nil, cursor, nil
default:
goto ERROR
}
}
ERROR:
return nil, 0, errNotAtBeginningOfValue(cursor)
}
}
}

View File

@ -566,17 +566,8 @@ func (d *structDecoder) decode(buf []byte, cursor, depth int64, p unsafe.Pointer
b := (*sliceHeader)(unsafe.Pointer(&buf)).data
switch char(b, cursor) {
case 'n':
if cursor+3 >= buflen {
return 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return 0, err
}
cursor += 4
return cursor, nil

View File

@ -1147,7 +1147,7 @@ var unmarshalTests = []unmarshalTest{
{in: `{"B":"true"}`, ptr: new(B), out: B{true}, golden: true}, // 127
{in: `{"B":"false"}`, ptr: new(B), out: B{false}, golden: true}, // 128
{in: `{"B": "maybe"}`, ptr: new(B), err: errors.New(`json: bool unexpected end of JSON input`)}, // 129
{in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid character as bool(true)`)}, // 130
{in: `{"B": "tru"}`, ptr: new(B), err: errors.New(`json: invalid character as true`)}, // 130
{in: `{"B": "False"}`, ptr: new(B), err: errors.New(`json: bool unexpected end of JSON input`)}, // 131
{in: `{"B": "null"}`, ptr: new(B), out: B{false}}, // 132
{in: `{"B": "nul"}`, ptr: new(B), err: errors.New(`json: invalid character as null`)}, // 133

View File

@ -97,10 +97,10 @@ func (d *uintDecoder) decodeStreamByte(s *stream) ([]byte, error) {
}
func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error) {
buflen := int64(len(buf))
for ; cursor < buflen; cursor++ {
for {
switch buf[cursor] {
case ' ', '\n', '\t', '\r':
cursor++
continue
case '0':
cursor++
@ -108,27 +108,14 @@ func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error
case '1', '2', '3', '4', '5', '6', '7', '8', '9':
start := cursor
cursor++
for ; cursor < buflen; cursor++ {
tk := int(buf[cursor])
if int('0') <= tk && tk <= int('9') {
continue
}
break
for numTable[buf[cursor]] {
cursor++
}
num := buf[start:cursor]
return num, cursor, nil
case 'n':
if cursor+3 >= buflen {
return nil, 0, errUnexpectedEndOfJSON("null", cursor)
}
if buf[cursor+1] != 'u' {
return nil, 0, errInvalidCharacter(buf[cursor+1], "null", cursor)
}
if buf[cursor+2] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+2], "null", cursor)
}
if buf[cursor+3] != 'l' {
return nil, 0, errInvalidCharacter(buf[cursor+3], "null", cursor)
if err := validateNull(buf, cursor); err != nil {
return nil, 0, err
}
cursor += 4
return nil, cursor, nil
@ -136,7 +123,6 @@ func (d *uintDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, error
return nil, 0, d.typeError([]byte{buf[cursor]}, cursor)
}
}
return nil, 0, errUnexpectedEndOfJSON("number(unsigned integer)", cursor)
}
func (d *uintDecoder) decodeStream(s *stream, depth int64, p unsafe.Pointer) error {