1
0
mirror of https://github.com/json-iterator/go.git synced 2024-11-27 08:30:57 +02:00

optimize read nil/true/false

This commit is contained in:
Tao Wen 2017-01-18 23:33:40 +08:00
parent 6efc6c44ac
commit a73e48e8bf
8 changed files with 216 additions and 202 deletions

View File

@ -10,7 +10,7 @@ func (iter *Iterator) ReadObject() (ret string) {
}
switch c {
case 'n':
iter.skipUntilBreak()
iter.skipFixedBytes(3)
if iter.Error != nil {
return
}

202
feature_iter_skip.go Normal file
View 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;
}
}

View File

@ -143,10 +143,6 @@ func (iter *Iterator) ReadStringAsSlice() (ret []byte) {
}
return copied
}
if c == 'n' {
iter.skipUntilBreak()
return
}
iter.reportError("ReadString", `expects " or n`)
return
}

View File

@ -140,7 +140,7 @@ type optionalEncoder struct {
func (encoder *optionalEncoder) encode(ptr unsafe.Pointer, stream *Stream) {
if *((*unsafe.Pointer)(ptr)) == nil {
stream.WriteNull()
stream.WriteNil()
} else {
encoder.valueEncoder.encode(*((*unsafe.Pointer)(ptr)), stream)
}
@ -322,6 +322,10 @@ func (iter *Iterator) ReadVal(obj interface{}) {
func (stream *Stream) WriteVal(val interface{}) {
if nil == val {
stream.WriteNil()
return
}
typ := reflect.TypeOf(val)
cacheKey := typ
cachedEncoder := getEncoderFromCache(cacheKey)

View File

@ -231,7 +231,7 @@ func (iter *Iterator) ReadArray() (ret bool) {
}
switch c {
case 'n':
iter.skipUntilBreak()
iter.skipFixedBytes(3)
return false // null
case '[':
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
func (iter *Iterator) ReadBase64() (ret []byte) {
@ -289,169 +271,3 @@ func (iter *Iterator) ReadBase64() (ret []byte) {
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
}
}
}

View File

@ -20,7 +20,6 @@ func Test_false(t *testing.T) {
}
}
func Test_write_true_false(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}

View File

@ -17,7 +17,7 @@ func Test_write_null(t *testing.T) {
should := require.New(t)
buf := &bytes.Buffer{}
stream := NewStream(buf, 4096)
stream.WriteNull()
stream.WriteNil()
stream.Flush()
should.Nil(stream.Error)
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) {
should := require.New(t)
iter := ParseString(`[null,"a"]`)
iter.ReadArray()
if iter.ReadString() != "" {
t.FailNow()
}
iter.ReadArray()
if iter.ReadString() != "a" {
t.FailNow()
}
should.True(iter.ReadArray())
should.True(iter.ReadNil())
should.True(iter.ReadArray())
should.Equal("a", iter.ReadString())
}
func Test_decode_null_skip(t *testing.T) {

View File

@ -134,7 +134,7 @@ func (b *Stream) WriteString(s string) {
b.writeByte('"')
}
func (stream *Stream) WriteNull() {
func (stream *Stream) WriteNil() {
stream.Write(bytesNull)
}