Kaynağa Gözat

Sort map keys when serialising map fields to binary and text formats.

This gives deterministic output, albeit somewhat inefficently.
I expect the inefficiency to be dwarfed by the reflection involved
anyway, but it's easy enough to improve this later if needed.
David Symonds 11 yıl önce
ebeveyn
işleme
7f07925444
3 değiştirilmiş dosya ile 17 ekleme ve 1 silme
  1. 3 1
      proto/encode.go
  2. 13 0
      proto/lib.go
  3. 1 0
      proto/text.go

+ 3 - 1
proto/encode.go

@@ -1101,7 +1101,9 @@ func (o *Buffer) enc_new_map(p *Properties, base structPointer) error {
 		return nil
 	}
 
-	for _, key := range v.MapKeys() {
+	keys := v.MapKeys()
+	sort.Sort(mapKeys(keys))
+	for _, key := range keys {
 		val := v.MapIndex(key)
 
 		keycopy.Set(key)

+ 13 - 0
proto/lib.go

@@ -736,3 +736,16 @@ func buildDefaultMessage(t reflect.Type) (dm defaultMessage) {
 
 	return dm
 }
+
+// Map fields may have key types of non-float scalars, strings and enums.
+// The easiest way to sort them in some deterministic order is to use fmt.
+// If this turns out to be inefficient we can always consider other options,
+// such as doing a Schwartzian transform.
+
+type mapKeys []reflect.Value
+
+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())
+}

+ 1 - 0
proto/text.go

@@ -247,6 +247,7 @@ func writeStruct(w *textWriter, sv reflect.Value) error {
 		if fv.Kind() == reflect.Map {
 			// Map fields are rendered as a repeated struct with key/value fields.
 			keys := fv.MapKeys() // TODO: should we sort these for deterministic output?
+			sort.Sort(mapKeys(keys))
 			for _, key := range keys {
 				val := fv.MapIndex(key)
 				if err := writeName(w, props); err != nil {