Skip to content

Commit

Permalink
fmtsort: make it work under Go 1.11
Browse files Browse the repository at this point in the history
It's not possible to make it work correctly in the face of
NaNs without MapRange, but we do the best we can.
  • Loading branch information
rogpeppe committed May 17, 2019
1 parent dc93391 commit f20e696
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 31 deletions.
2 changes: 2 additions & 0 deletions fmtsort/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ import "reflect"
func Compare(a, b reflect.Value) int {
return compare(a, b)
}

const BrokenNaNs = brokenNaNs
18 changes: 18 additions & 0 deletions fmtsort/mapelem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// +build go1.12

package fmtsort

import "reflect"

const brokenNaNs = false

func mapElems(mapValue reflect.Value) ([]reflect.Value, []reflect.Value) {
key := make([]reflect.Value, mapValue.Len())
value := make([]reflect.Value, len(key))
iter := mapValue.MapRange()
for i := 0; iter.Next(); i++ {
key[i] = iter.Key()
value[i] = iter.Value()
}
return key, value
}
23 changes: 23 additions & 0 deletions fmtsort/mapelem_1.11.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// +build !go1.12

package fmtsort

import "reflect"

const brokenNaNs = true

func mapElems(mapValue reflect.Value) ([]reflect.Value, []reflect.Value) {
key := mapValue.MapKeys()
value := make([]reflect.Value, len(key))
for i, k := range key {
v := mapValue.MapIndex(k)
if !v.IsValid() {
// Note: we can't retrieve the value, probably because
// the key is NaN, so just do the best we can and
// add a zero value of the correct type in that case.
v = reflect.Zero(mapValue.Type().Elem())
}
value[i] = v
}
return key, value
}
8 changes: 1 addition & 7 deletions fmtsort/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,7 @@ func Sort(mapValue reflect.Value) *SortedMap {
if mapValue.Type().Kind() != reflect.Map {
return nil
}
key := make([]reflect.Value, mapValue.Len())
value := make([]reflect.Value, len(key))
iter := mapValue.MapRange()
for i := 0; iter.Next(); i++ {
key[i] = iter.Key()
value[i] = iter.Value()
}
key, value := mapElems(mapValue)
sorted := &SortedMap{
Key: key,
Value: value,
Expand Down
55 changes: 31 additions & 24 deletions fmtsort/sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,50 +82,53 @@ func TestCompare(t *testing.T) {
}

type sortTest struct {
data interface{} // Always a map.
print string // Printed result using our custom printer.
data interface{} // Always a map.
print string // Printed result using our custom printer.
printBrokenNaNs string // Printed result when NaN support is broken (pre Go1.12).
}

var sortTests = []sortTest{
{
map[int]string{7: "bar", -3: "foo"},
"-3:foo 7:bar",
data: map[int]string{7: "bar", -3: "foo"},
print: "-3:foo 7:bar",
},
{
map[uint8]string{7: "bar", 3: "foo"},
"3:foo 7:bar",
data: map[uint8]string{7: "bar", 3: "foo"},
print: "3:foo 7:bar",
},
{
map[string]string{"7": "bar", "3": "foo"},
"3:foo 7:bar",
data: map[string]string{"7": "bar", "3": "foo"},
print: "3:foo 7:bar",
},
{
map[float64]string{7: "bar", -3: "foo", math.NaN(): "nan", math.Inf(0): "inf"},
"NaN:nan -3:foo 7:bar +Inf:inf",
data: map[float64]string{7: "bar", -3: "foo", math.NaN(): "nan", math.Inf(0): "inf"},
print: "NaN:nan -3:foo 7:bar +Inf:inf",
printBrokenNaNs: "NaN: -3:foo 7:bar +Inf:inf",
},
{
map[complex128]string{7 + 2i: "bar2", 7 + 1i: "bar", -3: "foo", complex(math.NaN(), 0i): "nan", complex(math.Inf(0), 0i): "inf"},
"(NaN+0i):nan (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf",
data: map[complex128]string{7 + 2i: "bar2", 7 + 1i: "bar", -3: "foo", complex(math.NaN(), 0i): "nan", complex(math.Inf(0), 0i): "inf"},
print: "(NaN+0i):nan (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf",
printBrokenNaNs: "(NaN+0i): (-3+0i):foo (7+1i):bar (7+2i):bar2 (+Inf+0i):inf",
},
{
map[bool]string{true: "true", false: "false"},
"false:false true:true",
data: map[bool]string{true: "true", false: "false"},
print: "false:false true:true",
},
{
chanMap(),
"CHAN0:0 CHAN1:1 CHAN2:2",
data: chanMap(),
print: "CHAN0:0 CHAN1:1 CHAN2:2",
},
{
pointerMap(),
"PTR0:0 PTR1:1 PTR2:2",
data: pointerMap(),
print: "PTR0:0 PTR1:1 PTR2:2",
},
{
map[toy]string{toy{7, 2}: "72", toy{7, 1}: "71", toy{3, 4}: "34"},
"{3 4}:34 {7 1}:71 {7 2}:72",
data: map[toy]string{toy{7, 2}: "72", toy{7, 1}: "71", toy{3, 4}: "34"},
print: "{3 4}:34 {7 1}:71 {7 2}:72",
},
{
map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"},
"[3 4]:34 [7 1]:71 [7 2]:72",
data: map[[2]int]string{{7, 2}: "72", {7, 1}: "71", {3, 4}: "34"},
print: "[3 4]:34 [7 1]:71 [7 2]:72",
},
}

Expand Down Expand Up @@ -202,8 +205,12 @@ type toy struct {
func TestOrder(t *testing.T) {
for _, test := range sortTests {
got := sprint(test.data)
if got != test.print {
t.Errorf("%s: got %q, want %q", reflect.TypeOf(test.data), got, test.print)
want := test.print
if fmtsort.BrokenNaNs && test.printBrokenNaNs != "" {
want = test.printBrokenNaNs
}
if got != want {
t.Errorf("%s: got %q, want %q", reflect.TypeOf(test.data), got, want)
}
}
}
Expand Down

0 comments on commit f20e696

Please sign in to comment.