goprotobuf: Serialize extensions in a consistent order.

R=golang-dev, iant
CC=golang-dev
https://codereview.appspot.com/6883047
diff --git a/proto/all_test.go b/proto/all_test.go
index 5024905..9f63d7c 100644
--- a/proto/all_test.go
+++ b/proto/all_test.go
@@ -1457,6 +1457,35 @@
 	}
 }
 
+func TestExtensionMarshalOrder(t *testing.T) {
+	m := &MyMessage{Count: Int32(123)}
+	if err := SetExtension(m, E_Ext_More, &Ext{Data: String("alpha")}); err != nil {
+		t.Fatalf("SetExtension: %v", err)
+	}
+	if err := SetExtension(m, E_Ext_Text, String("aleph")); err != nil {
+		t.Fatalf("SetExtension: %v", err)
+	}
+	if err := SetExtension(m, E_Ext_Number, Int32(1)); err != nil {
+		t.Fatalf("SetExtension: %v", err)
+	}
+
+	// Serialize m several times, and check we get the same bytes each time.
+	var orig []byte
+	for i := 0; i < 10; i++ {
+		b, err := Marshal(m)
+		if err != nil {
+			t.Fatalf("Marshal: %v", err)
+		}
+		if i == 0 {
+			orig = b
+			continue
+		}
+		if !bytes.Equal(b, orig) {
+			t.Errorf("Bytes differ on attempt #%d", i)
+		}
+	}
+}
+
 func fuzzUnmarshal(t *testing.T, data []byte) {
 	defer func() {
 		if e := recover(); e != nil {
diff --git a/proto/encode.go b/proto/encode.go
index 288fb9c..8ba3935 100644
--- a/proto/encode.go
+++ b/proto/encode.go
@@ -38,6 +38,7 @@
 import (
 	"errors"
 	"reflect"
+	"sort"
 )
 
 // ErrRequiredNotSet is the error returned if Marshal is called with
@@ -545,8 +546,23 @@
 	if err := encodeExtensionMap(v); err != nil {
 		return err
 	}
-	for _, e := range v {
-		o.buf = append(o.buf, e.enc...)
+	// Fast-path for common cases: zero or one extensions.
+	if len(v) <= 1 {
+		for _, e := range v {
+			o.buf = append(o.buf, e.enc...)
+		}
+		return nil
+	}
+
+	// Sort keys to provide a deterministic encoding.
+	keys := make([]int, 0, len(v))
+	for k := range v {
+		keys = append(keys, int(k))
+	}
+	sort.Ints(keys)
+
+	for _, k := range keys {
+		o.buf = append(o.buf, v[int32(k)].enc...)
 	}
 	return nil
 }