aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Symonds <dsymonds@golang.org>2015-01-09 11:09:00 +1100
committerDavid Symonds <dsymonds@golang.org>2015-01-09 15:17:43 +1100
commit7f07925444bb51fa4cf9dfe6f7661876f8852275 (patch)
treea8b61c01a21583a29a69a1c3ef7c09c15ebd5ff5
parent60976d353effd937b53a171e7493148a9969e39a (diff)
downloadprotobuf-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.go4
-rw-r--r--proto/lib.go13
-rw-r--r--proto/text.go1
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 {