You've already forked json-iterator
mirror of
https://github.com/json-iterator/go.git
synced 2025-06-15 22:50:24 +02:00
optimize read nil/true/false
This commit is contained in:
@ -10,7 +10,7 @@ func (iter *Iterator) ReadObject() (ret string) {
|
|||||||
}
|
}
|
||||||
switch c {
|
switch c {
|
||||||
case 'n':
|
case 'n':
|
||||||
iter.skipUntilBreak()
|
iter.skipFixedBytes(3)
|
||||||
if iter.Error != nil {
|
if iter.Error != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
202
feature_iter_skip.go
Normal file
202
feature_iter_skip.go
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
package jsoniter
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ReadNil reads a json object as nil and
|
||||||
|
// returns whether it's a nil or not
|
||||||
|
func (iter *Iterator) ReadNil() (ret bool) {
|
||||||
|
c := iter.nextToken()
|
||||||
|
if c == 'n' {
|
||||||
|
iter.skipFixedBytes(3) // null
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
iter.unreadByte()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadBool reads a json object as Bool
|
||||||
|
func (iter *Iterator) ReadBool() (ret bool) {
|
||||||
|
c := iter.nextToken()
|
||||||
|
if c == 't' {
|
||||||
|
iter.skipFixedBytes(3)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if c == 'f' {
|
||||||
|
iter.skipFixedBytes(4)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
iter.reportError("ReadBool", "expect t or f")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Skip skips a json object and positions to relatively the next json object
|
||||||
|
func (iter *Iterator) Skip() {
|
||||||
|
c := iter.nextToken()
|
||||||
|
switch c {
|
||||||
|
case '"':
|
||||||
|
iter.skipString()
|
||||||
|
case 'n', 't':
|
||||||
|
iter.skipFixedBytes(3) // null or true
|
||||||
|
case 'f':
|
||||||
|
iter.skipFixedBytes(4) // false
|
||||||
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
iter.skipUntilBreak()
|
||||||
|
case '[':
|
||||||
|
iter.skipArray()
|
||||||
|
case '{':
|
||||||
|
iter.skipObject()
|
||||||
|
default:
|
||||||
|
iter.reportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) skipString() {
|
||||||
|
for {
|
||||||
|
end, escaped := iter.findStringEnd()
|
||||||
|
if end == -1 {
|
||||||
|
if !iter.loadMore() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if escaped {
|
||||||
|
iter.head = 1 // skip the first char as last char read is \
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
iter.head = end
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
|
||||||
|
// Tries to find the end of string
|
||||||
|
// Support if string contains escaped quote symbols.
|
||||||
|
func (iter *Iterator) findStringEnd() (int, bool) {
|
||||||
|
escaped := false
|
||||||
|
for i := iter.head; i < iter.tail; i++ {
|
||||||
|
c := iter.buf[i]
|
||||||
|
if c == '"' {
|
||||||
|
if !escaped {
|
||||||
|
return i + 1, false
|
||||||
|
}
|
||||||
|
j := i - 1
|
||||||
|
for {
|
||||||
|
if j < iter.head || iter.buf[j] != '\\' {
|
||||||
|
// even number of backslashes
|
||||||
|
// either end of buffer, or " found
|
||||||
|
return i + 1, true
|
||||||
|
}
|
||||||
|
j--
|
||||||
|
if j < iter.head || iter.buf[j] != '\\' {
|
||||||
|
// odd number of backslashes
|
||||||
|
// it is \" or \\\"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
} else if c == '\\' {
|
||||||
|
escaped = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j := iter.tail - 1
|
||||||
|
for {
|
||||||
|
if j < iter.head || iter.buf[j] != '\\' {
|
||||||
|
// even number of backslashes
|
||||||
|
// either end of buffer, or " found
|
||||||
|
return -1, false // do not end with \
|
||||||
|
}
|
||||||
|
j--
|
||||||
|
if j < iter.head || iter.buf[j] != '\\' {
|
||||||
|
// odd number of backslashes
|
||||||
|
// it is \" or \\\"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
j--
|
||||||
|
|
||||||
|
}
|
||||||
|
return -1, true // end with \
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) skipArray() {
|
||||||
|
level := 1
|
||||||
|
for {
|
||||||
|
for i := iter.head; i < iter.tail; i++ {
|
||||||
|
switch iter.buf[i] {
|
||||||
|
case '"': // If inside string, skip it
|
||||||
|
iter.head = i + 1
|
||||||
|
iter.skipString()
|
||||||
|
i = iter.head - 1 // it will be i++ soon
|
||||||
|
case '[': // If open symbol, increase level
|
||||||
|
level++
|
||||||
|
case ']': // If close symbol, increase level
|
||||||
|
level--
|
||||||
|
|
||||||
|
// If we have returned to the original level, we're done
|
||||||
|
if level == 0 {
|
||||||
|
iter.head = i + 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !iter.loadMore() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) skipObject() {
|
||||||
|
level := 1
|
||||||
|
for {
|
||||||
|
for i := iter.head; i < iter.tail; i++ {
|
||||||
|
switch iter.buf[i] {
|
||||||
|
case '"': // If inside string, skip it
|
||||||
|
iter.head = i + 1
|
||||||
|
iter.skipString()
|
||||||
|
i = iter.head - 1 // it will be i++ soon
|
||||||
|
case '{': // If open symbol, increase level
|
||||||
|
level++
|
||||||
|
case '}': // If close symbol, increase level
|
||||||
|
level--
|
||||||
|
|
||||||
|
// If we have returned to the original level, we're done
|
||||||
|
if level == 0 {
|
||||||
|
iter.head = i + 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !iter.loadMore() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) skipUntilBreak() {
|
||||||
|
// true, false, null, number
|
||||||
|
for {
|
||||||
|
for i := iter.head; i < iter.tail; i++ {
|
||||||
|
c := iter.buf[i]
|
||||||
|
switch c {
|
||||||
|
case ' ', '\n', '\r', '\t', ',', '}', ']':
|
||||||
|
iter.head = i
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !iter.loadMore() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (iter *Iterator) skipFixedBytes(n int) {
|
||||||
|
iter.head += n;
|
||||||
|
if (iter.head >= iter.tail) {
|
||||||
|
more := iter.head - iter.tail;
|
||||||
|
if !iter.loadMore() {
|
||||||
|
iter.reportError("skipFixedBytes", "unexpected end");
|
||||||
|
return
|
||||||
|
}
|
||||||
|
iter.head += more;
|
||||||
|
}
|
||||||
|
}
|
@ -143,10 +143,6 @@ func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
|
|||||||
}
|
}
|
||||||
return copied
|
return copied
|
||||||
}
|
}
|
||||||
if c == 'n' {
|
|
||||||
iter.skipUntilBreak()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iter.reportError("ReadString", `expects " or n`)
|
iter.reportError("ReadString", `expects " or n`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ type optionalEncoder struct {
|
|||||||
|
|
||||||
func (encoder *optionalEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
func (encoder *optionalEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
|
||||||
if *((*unsafe.Pointer)(ptr)) == nil {
|
if *((*unsafe.Pointer)(ptr)) == nil {
|
||||||
stream.WriteNull()
|
stream.WriteNil()
|
||||||
} else {
|
} else {
|
||||||
encoder.valueEncoder.encode(*((*unsafe.Pointer)(ptr)), stream)
|
encoder.valueEncoder.encode(*((*unsafe.Pointer)(ptr)), stream)
|
||||||
}
|
}
|
||||||
@ -322,6 +322,10 @@ func (iter *Iterator) ReadVal(obj interface{}) {
|
|||||||
|
|
||||||
|
|
||||||
func (stream *Stream) WriteVal(val interface{}) {
|
func (stream *Stream) WriteVal(val interface{}) {
|
||||||
|
if nil == val {
|
||||||
|
stream.WriteNil()
|
||||||
|
return
|
||||||
|
}
|
||||||
typ := reflect.TypeOf(val)
|
typ := reflect.TypeOf(val)
|
||||||
cacheKey := typ
|
cacheKey := typ
|
||||||
cachedEncoder := getEncoderFromCache(cacheKey)
|
cachedEncoder := getEncoderFromCache(cacheKey)
|
||||||
|
186
iterator.go
186
iterator.go
@ -231,7 +231,7 @@ func (iter *Iterator) ReadArray() (ret bool) {
|
|||||||
}
|
}
|
||||||
switch c {
|
switch c {
|
||||||
case 'n':
|
case 'n':
|
||||||
iter.skipUntilBreak()
|
iter.skipFixedBytes(3)
|
||||||
return false // null
|
return false // null
|
||||||
case '[':
|
case '[':
|
||||||
c = iter.nextToken()
|
c = iter.nextToken()
|
||||||
@ -254,24 +254,6 @@ func (iter *Iterator) ReadArray() (ret bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ReadBool reads a json object as Bool
|
|
||||||
func (iter *Iterator) ReadBool() (ret bool) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if iter.Error != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch c {
|
|
||||||
case 't':
|
|
||||||
iter.skipUntilBreak()
|
|
||||||
return true
|
|
||||||
case 'f':
|
|
||||||
iter.skipUntilBreak()
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
iter.reportError("ReadBool", "expect t or f")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadBase64 reads a json object as Base64 in byte slice
|
// ReadBase64 reads a json object as Base64 in byte slice
|
||||||
func (iter *Iterator) ReadBase64() (ret []byte) {
|
func (iter *Iterator) ReadBase64() (ret []byte) {
|
||||||
@ -289,169 +271,3 @@ func (iter *Iterator) ReadBase64() (ret []byte) {
|
|||||||
return ret[:n]
|
return ret[:n]
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadNil reads a json object as nil and
|
|
||||||
// returns whether it's a nil or not
|
|
||||||
func (iter *Iterator) ReadNil() (ret bool) {
|
|
||||||
c := iter.nextToken()
|
|
||||||
if c == 'n' {
|
|
||||||
iter.skipUntilBreak()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
iter.unreadByte()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip skips a json object and positions to relatively the next json object
|
|
||||||
func (iter *Iterator) Skip() {
|
|
||||||
c := iter.nextToken()
|
|
||||||
switch c {
|
|
||||||
case '"':
|
|
||||||
iter.skipString()
|
|
||||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 't', 'f', 'n':
|
|
||||||
iter.skipUntilBreak()
|
|
||||||
case '[':
|
|
||||||
iter.skipArray()
|
|
||||||
case '{':
|
|
||||||
iter.skipObject()
|
|
||||||
default:
|
|
||||||
iter.reportError("Skip", fmt.Sprintf("do not know how to skip: %v", c))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipString() {
|
|
||||||
for {
|
|
||||||
end, escaped := iter.findStringEnd()
|
|
||||||
if end == -1 {
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if escaped {
|
|
||||||
iter.head = 1 // skip the first char as last char read is \
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
iter.head = end
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adapted from: https://github.com/buger/jsonparser/blob/master/parser.go
|
|
||||||
// Tries to find the end of string
|
|
||||||
// Support if string contains escaped quote symbols.
|
|
||||||
func (iter *Iterator) findStringEnd() (int, bool) {
|
|
||||||
escaped := false
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
if c == '"' {
|
|
||||||
if !escaped {
|
|
||||||
return i + 1, false
|
|
||||||
}
|
|
||||||
j := i - 1
|
|
||||||
for {
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// even number of backslashes
|
|
||||||
// either end of buffer, or " found
|
|
||||||
return i + 1, true
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// odd number of backslashes
|
|
||||||
// it is \" or \\\"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
}
|
|
||||||
} else if c == '\\' {
|
|
||||||
escaped = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j := iter.tail - 1
|
|
||||||
for {
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// even number of backslashes
|
|
||||||
// either end of buffer, or " found
|
|
||||||
return -1, false // do not end with \
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
if j < iter.head || iter.buf[j] != '\\' {
|
|
||||||
// odd number of backslashes
|
|
||||||
// it is \" or \\\"
|
|
||||||
break
|
|
||||||
}
|
|
||||||
j--
|
|
||||||
|
|
||||||
}
|
|
||||||
return -1, true // end with \
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipArray() {
|
|
||||||
level := 1
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
switch iter.buf[i] {
|
|
||||||
case '"': // If inside string, skip it
|
|
||||||
iter.head = i + 1
|
|
||||||
iter.skipString()
|
|
||||||
i = iter.head - 1 // it will be i++ soon
|
|
||||||
case '[': // If open symbol, increase level
|
|
||||||
level++
|
|
||||||
case ']': // If close symbol, increase level
|
|
||||||
level--
|
|
||||||
|
|
||||||
// If we have returned to the original level, we're done
|
|
||||||
if level == 0 {
|
|
||||||
iter.head = i + 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipObject() {
|
|
||||||
level := 1
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
switch iter.buf[i] {
|
|
||||||
case '"': // If inside string, skip it
|
|
||||||
iter.head = i + 1
|
|
||||||
iter.skipString()
|
|
||||||
i = iter.head - 1 // it will be i++ soon
|
|
||||||
case '{': // If open symbol, increase level
|
|
||||||
level++
|
|
||||||
case '}': // If close symbol, increase level
|
|
||||||
level--
|
|
||||||
|
|
||||||
// If we have returned to the original level, we're done
|
|
||||||
if level == 0 {
|
|
||||||
iter.head = i + 1
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (iter *Iterator) skipUntilBreak() {
|
|
||||||
// true, false, null, number
|
|
||||||
for {
|
|
||||||
for i := iter.head; i < iter.tail; i++ {
|
|
||||||
c := iter.buf[i]
|
|
||||||
switch c {
|
|
||||||
case ' ', '\n', '\r', '\t', ',', '}', ']':
|
|
||||||
iter.head = i
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !iter.loadMore() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -20,7 +20,6 @@ func Test_false(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func Test_write_true_false(t *testing.T) {
|
func Test_write_true_false(t *testing.T) {
|
||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
|
@ -17,7 +17,7 @@ func Test_write_null(t *testing.T) {
|
|||||||
should := require.New(t)
|
should := require.New(t)
|
||||||
buf := &bytes.Buffer{}
|
buf := &bytes.Buffer{}
|
||||||
stream := NewStream(buf, 4096)
|
stream := NewStream(buf, 4096)
|
||||||
stream.WriteNull()
|
stream.WriteNil()
|
||||||
stream.Flush()
|
stream.Flush()
|
||||||
should.Nil(stream.Error)
|
should.Nil(stream.Error)
|
||||||
should.Equal("null", buf.String())
|
should.Equal("null", buf.String())
|
||||||
@ -55,15 +55,12 @@ func Test_decode_null_array(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test_decode_null_string(t *testing.T) {
|
func Test_decode_null_string(t *testing.T) {
|
||||||
|
should := require.New(t)
|
||||||
iter := ParseString(`[null,"a"]`)
|
iter := ParseString(`[null,"a"]`)
|
||||||
iter.ReadArray()
|
should.True(iter.ReadArray())
|
||||||
if iter.ReadString() != "" {
|
should.True(iter.ReadNil())
|
||||||
t.FailNow()
|
should.True(iter.ReadArray())
|
||||||
}
|
should.Equal("a", iter.ReadString())
|
||||||
iter.ReadArray()
|
|
||||||
if iter.ReadString() != "a" {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_decode_null_skip(t *testing.T) {
|
func Test_decode_null_skip(t *testing.T) {
|
||||||
|
Reference in New Issue
Block a user