Sort numerically-keyed maps by numeric value.
This matches the C++ output and the specification
(https://developers.google.com/protocol-buffers/docs/proto#maps).
Technically we don't have to do this for the wire format,
but it's faster and less code to make them the same.
diff --git a/proto/lib.go b/proto/lib.go
index 0b28b08..95f7975 100644
--- a/proto/lib.go
+++ b/proto/lib.go
@@ -211,6 +211,7 @@
"fmt"
"log"
"reflect"
+ "sort"
"strconv"
"sync"
)
@@ -787,12 +788,39 @@
// If this turns out to be inefficient we can always consider other options,
// such as doing a Schwartzian transform.
-type mapKeys []reflect.Value
+func mapKeys(vs []reflect.Value) sort.Interface {
+ s := mapKeySorter{
+ vs: vs,
+ // default Less function: textual comparison
+ less: func(a, b reflect.Value) bool {
+ return fmt.Sprint(a.Interface()) < fmt.Sprint(b.Interface())
+ },
+ }
-func (s mapKeys) Len() int { return len(s) }
-func (s mapKeys) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
-func (s mapKeys) Less(i, j int) bool {
- return fmt.Sprint(s[i].Interface()) < fmt.Sprint(s[j].Interface())
+ // Type specialization per https://developers.google.com/protocol-buffers/docs/proto#maps;
+ // numeric keys are sorted numerically.
+ if len(vs) == 0 {
+ return s
+ }
+ switch vs[0].Kind() {
+ case reflect.Int32, reflect.Int64:
+ s.less = func(a, b reflect.Value) bool { return a.Int() < b.Int() }
+ case reflect.Uint32, reflect.Uint64:
+ s.less = func(a, b reflect.Value) bool { return a.Uint() < b.Uint() }
+ }
+
+ return s
+}
+
+type mapKeySorter struct {
+ vs []reflect.Value
+ less func(a, b reflect.Value) bool
+}
+
+func (s mapKeySorter) Len() int { return len(s.vs) }
+func (s mapKeySorter) Swap(i, j int) { s.vs[i], s.vs[j] = s.vs[j], s.vs[i] }
+func (s mapKeySorter) Less(i, j int) bool {
+ return s.less(s.vs[i], s.vs[j])
}
// isProto3Zero reports whether v is a zero proto3 value.