diff --git a/benchmarks/decode_test.go b/benchmarks/decode_test.go index 237e020..c274a31 100644 --- a/benchmarks/decode_test.go +++ b/benchmarks/decode_test.go @@ -118,7 +118,7 @@ func Benchmark_Decode_SmallStruct_Stream_GoJson(b *testing.B) { } } -func Benchmark_Decode_MediumStruct_EncodingJson(b *testing.B) { +func Benchmark_Decode_MediumStruct_Unmarshal_EncodingJson(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := MediumPayload{} @@ -128,7 +128,7 @@ func Benchmark_Decode_MediumStruct_EncodingJson(b *testing.B) { } } -func Benchmark_Decode_MediumStruct_JsonIter(b *testing.B) { +func Benchmark_Decode_MediumStruct_Unmarshal_JsonIter(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := MediumPayload{} @@ -138,7 +138,7 @@ func Benchmark_Decode_MediumStruct_JsonIter(b *testing.B) { } } -func Benchmark_Decode_MediumStruct_GoJay(b *testing.B) { +func Benchmark_Decode_MediumStruct_Unmarshal_GoJay(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := MediumPayload{} @@ -148,7 +148,7 @@ func Benchmark_Decode_MediumStruct_GoJay(b *testing.B) { } } -func Benchmark_Decode_MediumStruct_GoJayUnsafe(b *testing.B) { +func Benchmark_Decode_MediumStruct_Unmarshal_GoJayUnsafe(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := MediumPayload{} @@ -158,7 +158,7 @@ func Benchmark_Decode_MediumStruct_GoJayUnsafe(b *testing.B) { } } -func Benchmark_Decode_MediumStruct_GoJson(b *testing.B) { +func Benchmark_Decode_MediumStruct_Unmarshal_GoJson(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := MediumPayload{} @@ -168,7 +168,7 @@ func Benchmark_Decode_MediumStruct_GoJson(b *testing.B) { } } -func Benchmark_Decode_MediumStruct_GoJsonNoEscape(b *testing.B) { +func Benchmark_Decode_MediumStruct_Unmarshal_GoJsonNoEscape(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := MediumPayload{} @@ -178,7 +178,55 @@ func Benchmark_Decode_MediumStruct_GoJsonNoEscape(b *testing.B) { } } -func Benchmark_Decode_LargeStruct_EncodingJson(b *testing.B) { +func Benchmark_Decode_MediumStruct_Stream_EncodingJson(b *testing.B) { + b.ReportAllocs() + reader := bytes.NewReader(MediumFixture) + for i := 0; i < b.N; i++ { + result := MediumPayload{} + reader.Reset(MediumFixture) + if err := json.NewDecoder(reader).Decode(&result); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Decode_MediumStruct_Stream_JsonIter(b *testing.B) { + b.ReportAllocs() + reader := bytes.NewReader(MediumFixture) + for i := 0; i < b.N; i++ { + result := MediumPayload{} + reader.Reset(MediumFixture) + if err := jsoniter.NewDecoder(reader).Decode(&result); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Decode_MediumStruct_Stream_GoJay(b *testing.B) { + b.ReportAllocs() + reader := bytes.NewReader(MediumFixture) + for n := 0; n < b.N; n++ { + reader.Reset(MediumFixture) + result := MediumPayload{} + if err := gojay.NewDecoder(reader).DecodeObject(&result); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Decode_MediumStruct_Stream_GoJson(b *testing.B) { + b.ReportAllocs() + reader := bytes.NewReader(MediumFixture) + for i := 0; i < b.N; i++ { + result := MediumPayload{} + reader.Reset(MediumFixture) + if err := gojson.NewDecoder(reader).Decode(&result); err != nil { + b.Fatal(err) + } + } +} + +func Benchmark_Decode_LargeStruct_Unmarshal_EncodingJson(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := LargePayload{} @@ -188,7 +236,7 @@ func Benchmark_Decode_LargeStruct_EncodingJson(b *testing.B) { } } -func Benchmark_Decode_LargeStruct_JsonIter(b *testing.B) { +func Benchmark_Decode_LargeStruct_Unmarshal_JsonIter(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := LargePayload{} @@ -198,7 +246,7 @@ func Benchmark_Decode_LargeStruct_JsonIter(b *testing.B) { } } -func Benchmark_Decode_LargeStruct_GoJay(b *testing.B) { +func Benchmark_Decode_LargeStruct_Unmarshal_GoJay(b *testing.B) { b.ReportAllocs() for n := 0; n < b.N; n++ { result := LargePayload{} @@ -208,7 +256,7 @@ func Benchmark_Decode_LargeStruct_GoJay(b *testing.B) { } } -func Benchmark_Decode_LargeStruct_GoJayUnsafe(b *testing.B) { +func Benchmark_Decode_LargeStruct_Unmarshal_GoJayUnsafe(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := LargePayload{} @@ -218,7 +266,7 @@ func Benchmark_Decode_LargeStruct_GoJayUnsafe(b *testing.B) { } } -func Benchmark_Decode_LargeStruct_GoJson(b *testing.B) { +func Benchmark_Decode_LargeStruct_Unmarshal_GoJson(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := LargePayload{} @@ -228,7 +276,7 @@ func Benchmark_Decode_LargeStruct_GoJson(b *testing.B) { } } -func Benchmark_Decode_LargeStruct_GoJsonNoEscape(b *testing.B) { +func Benchmark_Decode_LargeStruct_Unmarshal_GoJsonNoEscape(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { result := LargePayload{} diff --git a/decode_context.go b/decode_context.go index 90371c4..dd68530 100644 --- a/decode_context.go +++ b/decode_context.go @@ -72,6 +72,63 @@ func skipValue(buf []byte, cursor int64) (int64, error) { return cursor, nil } continue + case 't': + 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) + } + cursor += 4 + if bracketCount == 0 && braceCount == 0 { + return cursor, nil + } + continue + case 'f': + 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) + } + cursor += 5 + if bracketCount == 0 && braceCount == 0 { + return cursor, nil + } + continue + case 'n': + if cursor+3 >= buflen { + return 0, errUnexpectedEndOfJSON("null", cursor) + } + if buf[cursor+1] != 'u' { + return 0, errUnexpectedEndOfJSON("null", cursor) + } + if buf[cursor+2] != 'l' { + return 0, errUnexpectedEndOfJSON("null", cursor) + } + if buf[cursor+3] != 'l' { + return 0, errUnexpectedEndOfJSON("null", cursor) + } + cursor += 4 + if bracketCount == 0 && braceCount == 0 { + return cursor, nil + } + continue } cursor++ } diff --git a/decode_stream.go b/decode_stream.go index acf8b49..e84b0b8 100644 --- a/decode_stream.go +++ b/decode_stream.go @@ -71,10 +71,16 @@ func (s *stream) read() bool { copy(newBuf, s.buf) copy(newBuf[s.length:], buf) s.buf = newBuf + s.length = totalSize - 1 + } else if s.length > 0 { + copy(buf[s.length:], buf) + copy(buf, s.buf[:s.length]) + s.buf = buf + s.length = totalSize - 1 } else { s.buf = buf + s.length = totalSize - 1 } - s.length = int64(len(s.buf)) - 1 s.offset += s.cursor if n == 0 { return false @@ -156,6 +162,30 @@ func (s *stream) skipValue() error { return nil } continue + case 't': + if err := trueBytes(s); err != nil { + return err + } + if bracketCount == 0 && braceCount == 0 { + return nil + } + continue + case 'f': + if err := falseBytes(s); err != nil { + return err + } + if bracketCount == 0 && braceCount == 0 { + return nil + } + continue + case 'n': + if err := nullBytes(s); err != nil { + return err + } + if bracketCount == 0 && braceCount == 0 { + return nil + } + continue } s.cursor++ } diff --git a/decode_string.go b/decode_string.go index b521d11..2c08153 100644 --- a/decode_string.go +++ b/decode_string.go @@ -88,7 +88,7 @@ func (d *stringDecoder) decodeStreamByte(s *stream) ([]byte, error) { if err := nullBytes(s); err != nil { return nil, err } - return []byte{'n', 'u', 'l', 'l'}, nil + return []byte{}, nil case nul: if s.read() { continue @@ -136,7 +136,7 @@ func (d *stringDecoder) decodeByte(buf []byte, cursor int64) ([]byte, int64, err return nil, 0, errInvalidCharacter(buf[cursor+3], "null", cursor) } cursor += 5 - return []byte{'n', 'u', 'l', 'l'}, cursor, nil + return []byte{}, cursor, nil default: goto ERROR }