Explorar el Código

implement readFloat32 fast path

Tao Wen hace 9 años
padre
commit
5b2a731963
Se han modificado 4 ficheros con 181 adiciones y 79 borrados
  1. 156 0
      feature_iter_float.go
  2. 1 70
      iterator.go
  3. 23 8
      jsoniter_float_test.go
  4. 1 1
      jsoniter_reflect_test.go

+ 156 - 0
feature_iter_float.go

@@ -0,0 +1,156 @@
+package jsoniter
+
+import (
+	"io"
+	"strconv"
+	"unsafe"
+)
+
+var zeroToNineDigits []int8
+const invalidCharForNumber = int8(-1)
+const endOfNumber = int8(-2)
+const dotInNumber = int8(-3)
+const safeToMultiple10 = uint64(0xffffffffffffffff) / 10 - 10
+
+func init() {
+	zeroToNineDigits = make([]int8, 256)
+	for i := 0; i < len(zeroToNineDigits); i++ {
+		zeroToNineDigits[i] = invalidCharForNumber
+	}
+	for i := int8('0'); i < int8('9'); i++ {
+		zeroToNineDigits[i] = i - int8('0')
+	}
+	zeroToNineDigits[','] = endOfNumber;
+	zeroToNineDigits[']'] = endOfNumber;
+	zeroToNineDigits['}'] = endOfNumber;
+	zeroToNineDigits[' '] = endOfNumber;
+	zeroToNineDigits['.'] = dotInNumber;
+}
+
+func (iter *Iterator) ReadFloat32() (ret float32) {
+	c := iter.nextToken()
+	if c == '-' {
+		return -iter.readPositiveFloat32()
+	} else {
+		iter.unreadByte()
+		return iter.readPositiveFloat32()
+	}
+}
+
+func (iter *Iterator) readPositiveFloat32() (ret float32) {
+	value := uint64(0)
+	c := byte(' ')
+	i := iter.head
+	non_decimal_loop:
+	for ; i < iter.tail; i++ {
+		c = iter.buf[i]
+		ind := zeroToNineDigits[c]
+		switch ind {
+		case invalidCharForNumber:
+			return iter.readFloat32SlowPath()
+		case endOfNumber:
+			iter.head = i
+			return float32(value)
+		case dotInNumber:
+			break non_decimal_loop
+		}
+		if value > safeToMultiple10 {
+			return iter.readFloat32SlowPath()
+		}
+		value = (value << 3) + (value << 1) + uint64(ind); // value = value * 10 + ind;
+	}
+	if c == '.' {
+		i++
+		decimalPlaces := 0;
+		for ; i < iter.tail; i++ {
+			c = iter.buf[i]
+			ind := zeroToNineDigits[c];
+			switch ind {
+			case endOfNumber:
+				if decimalPlaces > 0 && decimalPlaces < len(POW10) {
+					iter.head = i
+					return float32(float64(value) / float64(POW10[decimalPlaces]))
+				}
+				// too many decimal places
+			return iter.readFloat32SlowPath()
+			case invalidCharForNumber:
+				fallthrough
+			case dotInNumber:
+				return iter.readFloat32SlowPath()
+			}
+			decimalPlaces++
+			if value > safeToMultiple10 {
+				return iter.readFloat32SlowPath()
+			}
+			value = (value << 3) + (value << 1) + uint64(ind)
+		}
+	}
+	return iter.readFloat32SlowPath()
+}
+
+func (iter *Iterator) readFloat32SlowPath() (ret float32) {
+	strBuf := [16]byte{}
+	str := strBuf[0:0]
+	hasMore := true
+	for hasMore {
+		for i := iter.head; i < iter.tail; i++ {
+			c := iter.buf[i]
+			switch c {
+			case '-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+				str = append(str, c)
+				continue
+			default:
+				hasMore = false
+				break
+			}
+		}
+		if hasMore {
+			if !iter.loadMore() {
+				break
+			}
+		}
+	}
+	if iter.Error != nil && iter.Error != io.EOF {
+		return
+	}
+	val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 32)
+	if err != nil {
+		iter.Error = err
+		return
+	}
+	return float32(val)
+}
+
+// ReadFloat64 reads a json object as Float64
+func (iter *Iterator) ReadFloat64() (ret float64) {
+	strBuf := [8]byte{}
+	str := strBuf[0:0]
+	hasMore := true
+	for hasMore {
+		for i := iter.head; i < iter.tail; i++ {
+			c := iter.buf[i]
+			switch c {
+			case '-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+				str = append(str, c)
+				continue
+			default:
+				hasMore = false
+				break
+			}
+		}
+		if hasMore {
+			if !iter.loadMore() {
+				break
+			}
+		}
+	}
+	if iter.Error != nil && iter.Error != io.EOF {
+		return
+	}
+	val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 64)
+	if err != nil {
+		iter.Error = err
+		return
+	}
+	return val
+}

+ 1 - 70
iterator.go

@@ -4,9 +4,7 @@ import (
 	"encoding/base64"
 	"fmt"
 	"io"
-	"strconv"
 	"unicode/utf16"
-	"unsafe"
 )
 
 type ValueType int
@@ -71,7 +69,7 @@ type Iterator struct {
 }
 
 // Create creates an empty Iterator instance
-func Create() *Iterator {
+func NewIterator() *Iterator {
 	return &Iterator{
 		reader: nil,
 		buf:    nil,
@@ -570,73 +568,6 @@ func (iter *Iterator) ReadArray() (ret bool) {
 	}
 }
 
-// ReadFloat32 reads a json object as Float32
-func (iter *Iterator) ReadFloat32() (ret float32) {
-	strBuf := [8]byte{}
-	str := strBuf[0:0]
-	hasMore := true
-	for hasMore {
-		for i := iter.head; i < iter.tail; i++ {
-			c := iter.buf[i]
-			switch c {
-			case '-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-				str = append(str, c)
-				continue
-			default:
-				hasMore = false
-				break
-			}
-		}
-		if hasMore {
-			if !iter.loadMore() {
-				break
-			}
-		}
-	}
-	if iter.Error != nil && iter.Error != io.EOF {
-		return
-	}
-	val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 32)
-	if err != nil {
-		iter.Error = err
-		return
-	}
-	return float32(val)
-}
-
-// ReadFloat64 reads a json object as Float64
-func (iter *Iterator) ReadFloat64() (ret float64) {
-	strBuf := [8]byte{}
-	str := strBuf[0:0]
-	hasMore := true
-	for hasMore {
-		for i := iter.head; i < iter.tail; i++ {
-			c := iter.buf[i]
-			switch c {
-			case '-', '+', '.', 'e', 'E', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
-				str = append(str, c)
-				continue
-			default:
-				hasMore = false
-				break
-			}
-		}
-		if hasMore {
-			if !iter.loadMore() {
-				break
-			}
-		}
-	}
-	if iter.Error != nil && iter.Error != io.EOF {
-		return
-	}
-	val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&str)), 64)
-	if err != nil {
-		iter.Error = err
-		return
-	}
-	return val
-}
 
 // ReadBool reads a json object as Bool
 func (iter *Iterator) ReadBool() (ret bool) {

+ 23 - 8
jsoniter_float_test.go

@@ -25,12 +25,25 @@ func Test_float64_1_dot_1(t *testing.T) {
 	}
 }
 
-func Test_float32_1_dot_1_comma(t *testing.T) {
-	iter := ParseString(`1.1,`)
-	val := iter.ReadFloat32()
-	if val != 1.1 {
-		fmt.Println(iter.Error)
-		t.Fatal(val)
+func Test_read_float32(t *testing.T) {
+	inputs := []string{`1.1`, `1000`, `9223372036854775807`, `12.3`, `-12.3`, `720368.54775807`, `720368.547758075`}
+	for _, input := range inputs {
+		// non-streaming
+		t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
+			should := require.New(t)
+			iter := ParseString(input + ",")
+			expected, err := strconv.ParseFloat(input, 32)
+			should.Nil(err)
+			should.Equal(float32(expected), iter.ReadFloat32())
+		})
+		// streaming
+		t.Run(fmt.Sprintf("%v", input), func(t *testing.T) {
+			should := require.New(t)
+			iter := Parse(bytes.NewBufferString(input + ","), 2)
+			expected, err := strconv.ParseFloat(input, 32)
+			should.Nil(err)
+			should.Equal(float32(expected), iter.ReadFloat32())
+		})
 	}
 }
 
@@ -102,9 +115,11 @@ func Test_write_float64(t *testing.T) {
 
 func Benchmark_jsoniter_float(b *testing.B) {
 	b.ReportAllocs()
+	input := []byte(`1.1123,`)
+	iter := NewIterator()
 	for n := 0; n < b.N; n++ {
-		iter := ParseString(`1.1111111111`)
-		iter.ReadFloat64()
+		iter.ResetBytes(input)
+		iter.ReadFloat32()
 	}
 }
 

+ 1 - 1
jsoniter_reflect_test.go

@@ -70,7 +70,7 @@ type StructOfTagOne struct {
 
 func Benchmark_jsoniter_reflect(b *testing.B) {
 	b.ReportAllocs()
-	iter := Create()
+	iter := NewIterator()
 	Struct := &StructOfTagOne{}
 	//var Struct *StructOfTagOne
 	input := []byte(`{"field3": "100", "field4": "100"}`)