Przeglądaj źródła

Add support for uint64 and 64-bit binary notation

Add decode tests for uint,uint64,int,int64,float32,float64

Unmarshal uint64, use math constants for precision boundaries

Remove uint case, let uint64 cover uint overflows

Handle int64 and uint64 with 0b... notation

Make test cases contain literal values, reference math constants
Jordan Liggitt 11 lat temu
rodzic
commit
1dd72ac392
3 zmienionych plików z 144 dodań i 6 usunięć
  1. 18 4
      decode.go
  2. 108 0
      decode_test.go
  3. 18 2
      resolve.go

+ 18 - 4
decode.go

@@ -4,6 +4,7 @@ import (
 	"encoding"
 	"encoding/base64"
 	"fmt"
+	"math"
 	"reflect"
 	"strconv"
 	"time"
@@ -389,8 +390,13 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
 				out.SetInt(resolved)
 				good = true
 			}
+		case uint64:
+			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
+				out.SetInt(int64(resolved))
+				good = true
+			}
 		case float64:
-			if resolved < 1<<63-1 && !out.OverflowInt(int64(resolved)) {
+			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
 				out.SetInt(int64(resolved))
 				good = true
 			}
@@ -406,17 +412,22 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 		switch resolved := resolved.(type) {
 		case int:
-			if resolved >= 0 {
+			if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
 				out.SetUint(uint64(resolved))
 				good = true
 			}
 		case int64:
-			if resolved >= 0 {
+			if resolved >= 0 && !out.OverflowUint(uint64(resolved)) {
+				out.SetUint(uint64(resolved))
+				good = true
+			}
+		case uint64:
+			if !out.OverflowUint(uint64(resolved)) {
 				out.SetUint(uint64(resolved))
 				good = true
 			}
 		case float64:
-			if resolved < 1<<64-1 && !out.OverflowUint(uint64(resolved)) {
+			if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) {
 				out.SetUint(uint64(resolved))
 				good = true
 			}
@@ -435,6 +446,9 @@ func (d *decoder) scalar(n *node, out reflect.Value) (good bool) {
 		case int64:
 			out.SetFloat(float64(resolved))
 			good = true
+		case uint64:
+			out.SetFloat(float64(resolved))
+			good = true
 		case float64:
 			out.SetFloat(resolved)
 			good = true

+ 108 - 0
decode_test.go

@@ -267,6 +267,114 @@ var unmarshalTests = []struct {
 		map[string]uint64{},
 	},
 
+	// int
+	{
+		"int_max: 2147483647", // math.MaxInt32
+		map[string]int{"int_max": 2147483647},
+	},
+	{
+		"int_min: -2147483648", // math.MinInt32
+		map[string]int{"int_min": -2147483648},
+	},
+	{
+		"int_overflow: 9223372036854775808", // math.MaxInt64 + 1
+		map[string]int{},
+	},
+
+	// int64
+	{
+		"int64_max: 9223372036854775807", // math.MaxInt64
+		map[string]int64{"int64_max": 9223372036854775807},
+	},
+	{
+		"int64_max_base2: 0b111111111111111111111111111111111111111111111111111111111111111", // math.MaxInt64
+		map[string]int64{"int64_max_base2": 9223372036854775807},
+	},
+	{
+		"int64_min: -9223372036854775808", // math.MinInt64
+		map[string]int64{"int64_min": -9223372036854775808},
+	},
+	{
+		"int64_neg_base2: -0b111111111111111111111111111111111111111111111111111111111111111", // -math.MaxInt64
+		map[string]int64{"int64_neg_base2": -9223372036854775807},
+	},
+	{
+		"int64_overflow: 9223372036854775808", // math.MaxInt64 + 1
+		map[string]int64{},
+	},
+
+	// uint
+	{
+		"uint_min: 0",
+		map[string]uint{"uint_min": 0},
+	},
+	{
+		"uint_max: 4294967295", // math.MaxUint32
+		map[string]uint{"uint_max": 4294967295},
+	},
+	{
+		"uint_underflow: -1",
+		map[string]uint{},
+	},
+
+	// uint64
+	{
+		"uint64_min: 0",
+		map[string]uint{"uint64_min": 0},
+	},
+	{
+		"uint64_max: 18446744073709551615", // math.MaxUint64
+		map[string]uint64{"uint64_max": 18446744073709551615},
+	},
+	{
+		"uint64_max_base2: 0b1111111111111111111111111111111111111111111111111111111111111111", // math.MaxUint64
+		map[string]uint64{"uint64_max_base2": 18446744073709551615},
+	},
+	{
+		"uint64_maxint64: 9223372036854775807", // math.MaxInt64
+		map[string]uint64{"uint64_maxint64": 9223372036854775807},
+	},
+	{
+		"uint64_underflow: -1",
+		map[string]uint64{},
+	},
+
+	// float32
+	{
+		"float32_max: 3.40282346638528859811704183484516925440e+38", // math.MaxFloat32
+		map[string]float32{"float32_max": 3.40282346638528859811704183484516925440e+38},
+	},
+	{
+		"float32_nonzero: 1.401298464324817070923729583289916131280e-45", // math.SmallestNonzeroFloat32
+		map[string]float32{"float32_nonzero": 1.401298464324817070923729583289916131280e-45},
+	},
+	{
+		"float32_maxuint64: 18446744073709551615", // math.MaxUint64
+		map[string]float32{"float32_maxuint64": 1.8446744e+19},
+	},
+	{
+		"float32_maxuint64+1: 18446744073709551616", // math.MaxUint64 + 1
+		map[string]float32{"float32_maxuint64+1": 1.8446744e+19},
+	},
+
+	// float64
+	{
+		"float64_max: 1.797693134862315708145274237317043567981e+308", // math.MaxFloat64
+		map[string]float64{"float64_max": 1.797693134862315708145274237317043567981e+308},
+	},
+	{
+		"float64_nonzero: 4.940656458412465441765687928682213723651e-324", // math.SmallestNonzeroFloat64
+		map[string]float64{"float64_nonzero": 4.940656458412465441765687928682213723651e-324},
+	},
+	{
+		"float64_maxuint64: 18446744073709551615", // math.MaxUint64
+		map[string]float64{"float64_maxuint64": 1.8446744073709552e+19},
+	},
+	{
+		"float64_maxuint64+1: 18446744073709551616", // math.MaxUint64 + 1
+		map[string]float64{"float64_maxuint64+1": 1.8446744073709552e+19},
+	},
+
 	// Overflow cases.
 	{
 		"v: 4294967297",

+ 18 - 2
resolve.go

@@ -131,6 +131,10 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 					return yaml_INT_TAG, intv
 				}
 			}
+			uintv, err := strconv.ParseUint(plain, 0, 64)
+			if err == nil {
+				return yaml_INT_TAG, uintv
+			}
 			floatv, err := strconv.ParseFloat(plain, 64)
 			if err == nil {
 				return yaml_FLOAT_TAG, floatv
@@ -138,12 +142,24 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 			if strings.HasPrefix(plain, "0b") {
 				intv, err := strconv.ParseInt(plain[2:], 2, 64)
 				if err == nil {
-					return yaml_INT_TAG, int(intv)
+					if intv == int64(int(intv)) {
+						return yaml_INT_TAG, int(intv)
+					} else {
+						return yaml_INT_TAG, intv
+					}
+				}
+				uintv, err := strconv.ParseUint(plain[2:], 2, 64)
+				if err == nil {
+					return yaml_INT_TAG, uintv
 				}
 			} else if strings.HasPrefix(plain, "-0b") {
 				intv, err := strconv.ParseInt(plain[3:], 2, 64)
 				if err == nil {
-					return yaml_INT_TAG, -int(intv)
+					if intv == int64(int(intv)) {
+						return yaml_INT_TAG, -int(intv)
+					} else {
+						return yaml_INT_TAG, -intv
+					}
 				}
 			}
 			// XXX Handle timestamps here.