Browse Source

#68 handle float to int safely

Tao Wen 8 years ago
parent
commit
8ef0c22f25
2 changed files with 37 additions and 9 deletions
  1. 31 9
      extra/fuzzy_decoder.go
  2. 6 0
      extra/fuzzy_decoder_test.go

+ 31 - 9
extra/fuzzy_decoder.go

@@ -4,11 +4,28 @@ import (
 	"github.com/json-iterator/go"
 	"unsafe"
 	"encoding/json"
+	"strings"
 )
 
+const MaxUint = ^uint(0)
+const MinUint = 0
+const MaxInt = int(MaxUint >> 1)
+const MinInt = -MaxInt - 1
+
 func RegisterFuzzyDecoders() {
 	jsoniter.RegisterTypeDecoder("string", &FuzzyStringDecoder{})
-	jsoniter.RegisterTypeDecoder("int", &FuzzyIntDecoder{})
+	jsoniter.RegisterTypeDecoder("int", &FuzzyNumberDecoder{func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator, errorReporter *jsoniter.Iterator) {
+		if isFloat {
+			val := iter.ReadFloat64()
+			if val > float64(MaxInt) || val < float64(MinInt) {
+				errorReporter.ReportError("fuzzy decode int", "exceed range")
+				return
+			}
+			*((*int)(ptr)) = int(val)
+		} else {
+			*((*int)(ptr)) = iter.ReadInt()
+		}
+	}})
 }
 
 type FuzzyStringDecoder struct {
@@ -28,20 +45,25 @@ func (decoder *FuzzyStringDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Ite
 	}
 }
 
-type FuzzyIntDecoder struct {
+type FuzzyNumberDecoder struct {
+	fun func(isFloat bool, ptr unsafe.Pointer, iter *jsoniter.Iterator, errorReporter *jsoniter.Iterator)
 }
 
-func (decoder *FuzzyIntDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
+func (decoder *FuzzyNumberDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
 	valueType := iter.WhatIsNext()
+	var str string
 	switch valueType {
 	case jsoniter.Number:
-		// use current iterator
+		var number json.Number
+		iter.ReadVal(&number)
+		str = string(number)
 	case jsoniter.String:
-		str := iter.ReadString()
-		iter = iter.Config().BorrowIterator([]byte(str))
-		defer iter.Config().ReturnIterator(iter)
+		str = iter.ReadString()
 	default:
-		iter.ReportError("FuzzyIntDecoder", "not number or string")
+		iter.ReportError("FuzzyNumberDecoder", "not number or string")
 	}
-	*((*int)(ptr)) = int(iter.ReadFloat64())
+	newIter := iter.Config().BorrowIterator([]byte(str))
+	defer iter.Config().ReturnIterator(newIter)
+	isFloat := strings.IndexByte(str, '.') != -1
+	decoder.fun(isFloat, ptr, newIter, iter)
 }

+ 6 - 0
extra/fuzzy_decoder_test.go

@@ -51,3 +51,9 @@ func Test_float_to_int(t *testing.T) {
 	should.Nil(jsoniter.UnmarshalFromString(`1.23`, &val))
 	should.Equal(1, val)
 }
+
+func Test_large_float_to_int(t *testing.T) {
+	should := require.New(t)
+	var val int
+	should.NotNil(jsoniter.UnmarshalFromString(`1234512345123451234512345.0`, &val))
+}