diff options
author | David Symonds <dsymonds@golang.org> | 2015-01-09 11:09:00 +1100 |
---|---|---|
committer | David Symonds <dsymonds@golang.org> | 2015-01-09 15:17:43 +1100 |
commit | 7f07925444bb51fa4cf9dfe6f7661876f8852275 (patch) | |
tree | a8b61c01a21583a29a69a1c3ef7c09c15ebd5ff5 | |
parent | 60976d353effd937b53a171e7493148a9969e39a (diff) | |
download | protobuf-7f07925444bb51fa4cf9dfe6f7661876f8852275.tar.gz |
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.
-rw-r--r-- | proto/encode.go | 4 | ||||
-rw-r--r-- | proto/lib.go | 13 | ||||
-rw-r--r-- | proto/text.go | 1 |
3 files changed, 17 insertions, 1 deletions
diff --git a/proto/encode.go b/proto/encode.go index f5050e3..1512d60 100644 --- a/proto/encode.go +++ b/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) diff --git a/proto/lib.go b/proto/lib.go index fe0818c..87c6b9d 100644 --- a/proto/lib.go +++ b/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()) +} diff --git a/proto/text.go b/proto/text.go index f41a946..720eac4 100644 --- a/proto/text.go +++ b/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 { |