diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c8a76d7..4f4d9f7 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -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 diff --git a/decode_array.go b/decode_array.go index 92b9dd9..508f097 100644 --- a/decode_array.go +++ b/decode_array.go @@ -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) } } - return 0, errUnexpectedEndOfJSON("array", cursor) } diff --git a/decode_bool.go b/decode_bool.go index d818896..9efc1c1 100644 --- a/decode_bool.go +++ b/decode_bool.go @@ -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 diff --git a/decode_bytes.go b/decode_bytes.go index b5e34a3..e0b8b8a 100644 --- a/decode_bytes.go +++ b/decode_bytes.go @@ -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 + return nil, 0, errNotAtBeginningOfValue(cursor) } } -ERROR: - return nil, 0, errNotAtBeginningOfValue(cursor) } diff --git a/decode_context.go b/decode_context.go index 38ab7c8..a6ff091 100644 --- a/decode_context.go +++ b/decode_context.go @@ -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) diff --git a/decode_float.go b/decode_float.go index 6c2a390..d48c2c8 100644 --- a/decode_float.go +++ b/decode_float.go @@ -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 { diff --git a/decode_int.go b/decode_int.go index 8db98a9..625cc06 100644 --- a/decode_int.go +++ b/decode_int.go @@ -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 diff --git a/decode_interface.go b/decode_interface.go index fee2cdb..e7e96b8 100644 --- a/decode_interface.go +++ b/decode_interface.go @@ -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 diff --git a/decode_map.go b/decode_map.go index a3fa291..91af50e 100644 --- a/decode_map.go +++ b/decode_map.go @@ -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 diff --git a/decode_number.go b/decode_number.go index 07022f8..6c8720b 100644 --- a/decode_number.go +++ b/decode_number.go @@ -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) } diff --git a/decode_ptr.go b/decode_ptr.go index ac4af6f..40997c5 100644 --- a/decode_ptr.go +++ b/decode_ptr.go @@ -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 diff --git a/decode_slice.go b/decode_slice.go index df79aab..b546c49 100644 --- a/decode_slice.go +++ b/decode_slice.go @@ -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 + return 0, errUnexpectedEndOfJSON("slice", cursor) } } -ERROR: - return 0, errUnexpectedEndOfJSON("slice", cursor) } diff --git a/decode_string.go b/decode_string.go index 8f1b0e5..3ca1710 100644 --- a/decode_string.go +++ b/decode_string.go @@ -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 + return nil, 0, errNotAtBeginningOfValue(cursor) } } -ERROR: - return nil, 0, errNotAtBeginningOfValue(cursor) } diff --git a/decode_struct.go b/decode_struct.go index da32d6f..f857bfc 100644 --- a/decode_struct.go +++ b/decode_struct.go @@ -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 diff --git a/decode_test.go b/decode_test.go index bcdff45..cdacbc5 100644 --- a/decode_test.go +++ b/decode_test.go @@ -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 diff --git a/decode_uint.go b/decode_uint.go index 2d06e06..91a0004 100644 --- a/decode_uint.go +++ b/decode_uint.go @@ -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 {