Pārlūkot izejas kodu

optimize read nil/true/false

Tao Wen 9 gadi atpakaļ
vecāks
revīzija
a73e48e8bf
8 mainītis faili ar 216 papildinājumiem un 202 dzēšanām
  1. 1 1
      feature_iter_object.go
  2. 202 0
      feature_iter_skip.go
  3. 0 4
      feature_iter_string.go
  4. 5 1
      feature_reflect.go
  5. 1 185
      iterator.go
  6. 0 1
      jsoniter_bool_test.go
  7. 6 9
      jsoniter_null_test.go
  8. 1 1
      stream.go

+ 1 - 1
feature_iter_object.go

@@ -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 - 0
feature_iter_skip.go

@@ -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;
+	}
+}

+ 0 - 4
feature_iter_string.go

@@ -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
 }

+ 5 - 1
feature_reflect.go

@@ -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)

+ 1 - 185
iterator.go

@@ -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
-		}
-	}
-}

+ 0 - 1
jsoniter_bool_test.go

@@ -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{}

+ 6 - 9
jsoniter_null_test.go

@@ -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) {

+ 1 - 1
stream.go

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