Disallow floating point numbers.

Floating point numbers are not allowed in canonical JSON.
Neither are leading zeros or "minus 0" for integers.

according to http://wiki.laptop.org/go/Canonical_JSON

Signed-off-by: Jessica Frazelle <acidburn@docker.com>
This commit is contained in:
Jessica Frazelle 2015-08-01 20:51:31 -07:00
Родитель e71362867b
Коммит 51c947c0fd
5 изменённых файлов: 45 добавлений и 33 удалений

Просмотреть файл

@ -36,14 +36,14 @@ type V struct {
var ifaceNumAsFloat64 = map[string]interface{}{
"k1": float64(1),
"k2": "s",
"k3": []interface{}{float64(1), float64(2.0), float64(3e-3)},
"k3": []interface{}{float64(1), float64(2.0), float64(3)},
"k4": map[string]interface{}{"kk1": "s", "kk2": float64(2)},
}
var ifaceNumAsNumber = map[string]interface{}{
"k1": Number("1"),
"k2": "s",
"k3": []interface{}{Number("1"), Number("2.0"), Number("3e-3")},
"k3": []interface{}{Number("1"), Number("2.0"), Number("3")},
"k4": map[string]interface{}{"kk1": "s", "kk2": Number("2")},
}
@ -220,7 +220,7 @@ var unmarshalTests = []unmarshalTest{
// basic types
{in: `true`, ptr: new(bool), out: true},
{in: `1`, ptr: new(int), out: 1},
{in: `1.2`, ptr: new(float64), out: 1.2},
{in: `1.0`, ptr: new(float64), out: 1.0},
{in: `-5`, ptr: new(int16), out: int16(-5)},
{in: `2`, ptr: new(Number), out: Number("2"), useNumber: true},
{in: `2`, ptr: new(Number), out: Number("2")},
@ -235,13 +235,13 @@ var unmarshalTests = []unmarshalTest{
{in: `{"x": 1}`, ptr: new(tx), out: tx{}},
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: float64(1), F2: int32(2), F3: Number("3")}},
{in: `{"F1":1,"F2":2,"F3":3}`, ptr: new(V), out: V{F1: Number("1"), F2: int32(2), F3: Number("3")}, useNumber: true},
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64},
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3e-3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true},
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsFloat64},
{in: `{"k1":1,"k2":"s","k3":[1,2.0,3],"k4":{"kk1":"s","kk2":2}}`, ptr: new(interface{}), out: ifaceNumAsNumber, useNumber: true},
// raw values with whitespace
{in: "\n true ", ptr: new(bool), out: true},
{in: "\t 1 ", ptr: new(int), out: 1},
{in: "\r 1.2 ", ptr: new(float64), out: 1.2},
{in: "\r 1.0 ", ptr: new(float64), out: 1.0},
{in: "\t -5 \n", ptr: new(int16), out: int16(-5)},
{in: "\t \"a\\u1234\" \n", ptr: new(string), out: "a\u1234"},
@ -796,8 +796,8 @@ var allValue = All{
Uint32: 10,
Uint64: 11,
Uintptr: 12,
Float32: 14.1,
Float64: 15.1,
Float32: 14,
Float64: 15,
Foo: "foo",
Foo2: "foo2",
IntStr: 42,
@ -818,7 +818,7 @@ var allValue = All{
ByteSlice: []byte{27, 28, 29},
Small: Small{Tag: "tag30"},
PSmall: &Small{Tag: "tag31"},
Interface: 5.2,
Interface: 5.0,
}
var pallValue = All{
@ -858,8 +858,8 @@ var allValueIndent = `{
"Uint32": 10,
"Uint64": 11,
"Uintptr": 12,
"Float32": 14.1,
"Float64": 15.1,
"Float32": 14,
"Float64": 15,
"bar": "foo",
"bar2": "foo2",
"IntStr": "42",
@ -931,7 +931,7 @@ var allValueIndent = `{
"Tag": "tag31"
},
"PPSmall": null,
"Interface": 5.2,
"Interface": 5,
"PInterface": null
}`
@ -967,8 +967,8 @@ var pallValueIndent = `{
"PUint32": 10,
"PUint64": 11,
"PUintptr": 12,
"PFloat32": 14.1,
"PFloat64": 15.1,
"PFloat32": 14,
"PFloat64": 15,
"String": "",
"PString": "16",
"Map": null,
@ -1020,7 +1020,7 @@ var pallValueIndent = `{
"Tag": "tag31"
},
"Interface": null,
"PInterface": 5.2
"PInterface": 5
}`
var pallValueCompact = strings.Map(noSpace, pallValueIndent)
@ -1169,8 +1169,8 @@ func TestUnmarshalNulls(t *testing.T) {
Uint16: 9,
Uint32: 10,
Uint64: 11,
Float32: 12.1,
Float64: 13.1,
Float32: 12.0,
Float64: 13.0,
String: "14"}
err := Unmarshal(jsonData, &nulls)
@ -1179,7 +1179,7 @@ func TestUnmarshalNulls(t *testing.T) {
}
if !nulls.Bool || nulls.Int != 2 || nulls.Int8 != 3 || nulls.Int16 != 4 || nulls.Int32 != 5 || nulls.Int64 != 6 ||
nulls.Uint != 7 || nulls.Uint8 != 8 || nulls.Uint16 != 9 || nulls.Uint32 != 10 || nulls.Uint64 != 11 ||
nulls.Float32 != 12.1 || nulls.Float64 != 13.1 || nulls.String != "14" {
nulls.Float32 != 12.0 || nulls.Float64 != 13.0 || nulls.String != "14" {
t.Errorf("Unmarshal of null values affected primitives")
}
@ -1327,13 +1327,13 @@ func TestPrefilled(t *testing.T) {
}{
{
in: `{"X": 1, "Y": 2}`,
ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1.5},
out: &XYZ{X: float64(1), Y: float64(2), Z: 1.5},
ptr: &XYZ{X: float32(3), Y: int16(4), Z: 1},
out: &XYZ{X: float64(1), Y: float64(2), Z: 1},
},
{
in: `{"X": 1, "Y": 2}`,
ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1.5}),
out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1.5}),
ptr: ptrToMap(map[string]interface{}{"X": float32(3), "Y": int16(4), "Z": 1}),
out: ptrToMap(map[string]interface{}{"X": float64(1), "Y": float64(2), "Z": 1}),
},
}

Просмотреть файл

@ -14,7 +14,7 @@ import (
"bytes"
"encoding"
"encoding/base64"
"encoding/json"
"fmt"
"math"
"reflect"
"runtime"
@ -512,8 +512,11 @@ type floatEncoder int // number of bits
func (bits floatEncoder) encode(e *encodeState, v reflect.Value, quoted bool) {
f := v.Float()
if math.IsInf(f, 0) || math.IsNaN(f) {
e.error(&UnsupportedValueError{v, strconv.FormatFloat(f, 'g', -1, int(bits))})
if math.IsInf(f, 0) || math.IsNaN(f) || math.Floor(f) != f {
e.error(&UnsupportedValueError{
v,
fmt.Sprintf("floating point number, %s", strconv.FormatFloat(f, 'g', -1, int(bits))),
})
}
b := strconv.AppendFloat(e.scratch[:0], f, 'g', -1, int(bits))
if quoted {
@ -619,7 +622,7 @@ func canonicalEncoder(e *encodeState, v reflect.Value, _ bool) {
b = bytes.TrimSpace(b)
if len(b) > 0 && b[0] == '{' {
var temp interface{}
err = json.Unmarshal(b, &temp)
err = Unmarshal(b, &temp)
if err != nil {
e.error(&MarshalerError{v.Type(), err})
}

Просмотреть файл

@ -586,3 +586,12 @@ func TestCanonicalization(t *testing.T) {
t.Errorf("got %s, want %s", string(output), expected)
}
}
func TestFloatError(t *testing.T) {
input := struct{ A float64 }{1.1}
_, err := Marshal(input)
if err == nil {
t.Errorf("want float error, got nil")
}
}

Просмотреть файл

@ -264,7 +264,7 @@ func genValue(n int) interface{} {
case 0:
return rand.Intn(2) == 0
case 1:
return rand.NormFloat64()
return math.Floor(rand.NormFloat64())
case 2:
return genString(30)
}

Просмотреть файл

@ -16,24 +16,24 @@ import (
// Test values for the stream test.
// One of each JSON kind.
var streamTest = []interface{}{
0.1,
1.0,
"hello",
nil,
true,
false,
[]interface{}{"a", "b", "c"},
map[string]interface{}{"": "Kelvin", "ß": "long s"},
3.14, // another value to make sure something can follow map
3.0, // another value to make sure something can follow map
}
var streamEncoded = `0.1
var streamEncoded = `1
"hello"
null
true
false
["a","b","c"]
{"ß":"long s","":"Kelvin"}
3.14
3
`
func TestEncoder(t *testing.T) {
@ -129,7 +129,7 @@ func TestRawMessage(t *testing.T) {
Y float32
}
const raw = `["\u0056",null]`
const msg = `{"X":0.1,"Id":["\u0056",null],"Y":0.2}`
const msg = `{"X":1,"Id":["\u0056",null],"Y":2}`
err := Unmarshal([]byte(msg), &data)
if err != nil {
t.Fatalf("Unmarshal: %v", err)
@ -154,7 +154,7 @@ func TestNullRawMessage(t *testing.T) {
Y float32
}
data.Id = new(RawMessage)
const msg = `{"X":0.1,"Id":null,"Y":0.2}`
const msg = `{"X":1,"Id":null,"Y":2}`
err := Unmarshal([]byte(msg), &data)
if err != nil {
t.Fatalf("Unmarshal: %v", err)