From c457aeaac26cf9e696b83f177822fd55c15a612b Mon Sep 17 00:00:00 2001 From: Tao Wen Date: Tue, 6 Dec 2016 13:48:03 +0800 Subject: [PATCH] optimize --- jsoniter.go | 88 ++++++++++++++++++++++++++++++------------ jsoniter_array_test.go | 31 ++++++++++++++- 2 files changed, 92 insertions(+), 27 deletions(-) diff --git a/jsoniter.go b/jsoniter.go index 2d038f6..7644515 100644 --- a/jsoniter.go +++ b/jsoniter.go @@ -26,7 +26,6 @@ func init() { } } - type Iterator struct { reader io.Reader buf []byte @@ -57,14 +56,30 @@ func ParseBytes(input []byte) *Iterator { return iter } +func (iter *Iterator) Reuse(input []byte) *Iterator { + // only for benchmarking + iter.reader = nil + iter.Error = nil + iter.buf = input + iter.head = 0 + iter.tail = len(input) + iter.skipWhitespaces() + return iter +} + func ParseString(input string) *Iterator { return ParseBytes([]byte(input)) } func (iter *Iterator) skipWhitespaces() { c := iter.readByte() - for c == ' ' || c == '\n' || c == '\t' { - c = iter.readByte() + for { + switch c { + case ' ', '\n', '\t', 'r': + c = iter.readByte() + continue + } + break } iter.unreadByte() } @@ -258,29 +273,20 @@ func (iter *Iterator) ReadInt64() (ret int64) { func (iter *Iterator) ReadString() (ret string) { str := make([]byte, 0, 8) c := iter.readByte() - if iter.Error != nil { + if c == 'n' { + iter.skipNull() return } - switch c { - case 'n': - iter.skipNull() - if iter.Error != nil { - return - } - return "" - case '"': - // nothing - default: + if c != '"' { iter.ReportError("ReadString", `expects " or n`) return } - for { + for iter.Error == nil { c = iter.readByte() - if iter.Error != nil { - return + if c == '"' { + return string(str) } - switch c { - case '\\': + if c == '\\' { c = iter.readByte() if iter.Error != nil { return @@ -340,12 +346,11 @@ func (iter *Iterator) ReadString() (ret string) { `invalid escape char after \`) return } - case '"': - return *(*string)(unsafe.Pointer(&str)) - default: + } else { str = append(str, c) } } + return } func (iter *Iterator) readU4() (ret rune) { @@ -434,9 +439,6 @@ func (iter *Iterator) ReadArray() (ret bool) { switch c { case 'n': { iter.skipNull() - if iter.Error != nil { - return - } return false // null } case '[': { @@ -462,6 +464,42 @@ func (iter *Iterator) ReadArray() (ret bool) { } } +func (iter *Iterator) ReadArrayCB(cb func()) { + iter.skipWhitespaces() + c := iter.readByte() + if c == 'n' { + iter.skipNull() + return // null + } + if c != '[' { + iter.ReportError("ReadArray", "expect [ or n") + return + } + iter.skipWhitespaces() + c = iter.readByte() + if c == ']' { + return // [] + } else { + iter.unreadByte() + } + for { + if iter.Error != nil { + return + } + cb() + iter.skipWhitespaces() + c = iter.readByte() + if c == ']' { + return + } + if c != ',' { + iter.ReportError("ReadArray", "expect , or ]") + return + } + iter.skipWhitespaces() + } +} + func (iter *Iterator) ReadObject() (ret string) { iter.skipWhitespaces() c := iter.readByte() diff --git a/jsoniter_array_test.go b/jsoniter_array_test.go index 87604a4..ffbf225 100644 --- a/jsoniter_array_test.go +++ b/jsoniter_array_test.go @@ -50,6 +50,17 @@ func Test_two_elements(t *testing.T) { } } +func Test_two_elements_cb(t *testing.T) { + iter := ParseString(`[1,2]`) + total := int64(0) + iter.ReadArrayCB(func() { + total += iter.ReadInt64() + }) + if total != 3 { + t.Fatal(total) + } +} + func Test_invalid_array(t *testing.T) { iter := ParseString(`[`) iter.ReadArray() @@ -117,16 +128,32 @@ func Test_whitespace_before_comma(t *testing.T) { } } - func Benchmark_jsoniter_array(b *testing.B) { + b.ReportAllocs() + input := []byte(`[1,2,3,4,5,6,7,8,9]`) + iter := ParseBytes(input) + b.ResetTimer() for n := 0; n < b.N; n++ { - iter := ParseString(`[1,2,3]`) + iter.Reuse(input) for iter.ReadArray() { iter.ReadUint64() } } } +func Benchmark_jsoniter_array_cb(b *testing.B) { + b.ReportAllocs() + input := []byte(`[1,2,3,4,5,6,7,8,9]`) + iter := ParseBytes(input) + b.ResetTimer() + for n := 0; n < b.N; n++ { + iter.Reuse(input) + iter.ReadArrayCB(func() { + iter.ReadUint64() + }) + } +} + func Benchmark_json_array(b *testing.B) { for n := 0; n < b.N; n++ { result := []interface{}{}