proto: amortize cost of growing a Buffer (#584)

Fixes #566
diff --git a/proto/all_test.go b/proto/all_test.go
index e7c1ff9..361f72f 100644
--- a/proto/all_test.go
+++ b/proto/all_test.go
@@ -363,6 +363,48 @@
 	}
 }
 
+// Ensure that Buffer.Marshal uses O(N) memory for N messages
+func TestBufferMarshalAllocs(t *testing.T) {
+	value := &OtherMessage{Key: Int64(1)}
+	msg := &MyMessage{Count: Int32(1), Others: []*OtherMessage{value}}
+
+	reallocSize := func(t *testing.T, items int, prealloc int) (int64, int64) {
+		var b Buffer
+		b.SetBuf(make([]byte, 0, prealloc))
+
+		var allocSpace int64
+		prevCap := cap(b.Bytes())
+		for i := 0; i < items; i++ {
+			err := b.Marshal(msg)
+			if err != nil {
+				t.Errorf("Marshal err = %q", err)
+				break
+			}
+			if c := cap(b.Bytes()); prevCap != c {
+				allocSpace += int64(c)
+				prevCap = c
+			}
+		}
+		needSpace := int64(len(b.Bytes()))
+		return allocSpace, needSpace
+	}
+
+	for _, prealloc := range []int{0, 100, 10000} {
+		for _, items := range []int{1, 2, 5, 10, 20, 50, 100, 200, 500, 1000} {
+			runtimeSpace, need := reallocSize(t, items, prealloc)
+			totalSpace := int64(prealloc) + runtimeSpace
+
+			runtimeRatio := float64(runtimeSpace) / float64(need)
+			totalRatio := float64(totalSpace) / float64(need)
+
+			if totalRatio < 1 || runtimeRatio > 4 {
+				t.Errorf("needed %dB, allocated %dB total (ratio %.1f), allocated %dB at runtime (ratio %.1f)",
+					need, totalSpace, totalRatio, runtimeSpace, runtimeRatio)
+			}
+		}
+	}
+}
+
 // Simple tests for bytes
 func TestBytesPrimitives(t *testing.T) {
 	o := old()
diff --git a/proto/table_marshal.go b/proto/table_marshal.go
index 095c13c..0f212b3 100644
--- a/proto/table_marshal.go
+++ b/proto/table_marshal.go
@@ -2643,10 +2643,7 @@
 	var err error
 	if m, ok := pb.(newMarshaler); ok {
 		siz := m.XXX_Size()
-		// make sure buf has enough capacity
-		if newCap := len(p.buf) + siz; newCap > cap(p.buf) {
-			p.buf = append(make([]byte, 0, newCap), p.buf...)
-		}
+		p.grow(siz) // make sure buf has enough capacity
 		p.buf, err = m.XXX_Marshal(p.buf, p.deterministic)
 		return err
 	}
@@ -2663,10 +2660,22 @@
 	}
 	var info InternalMessageInfo
 	siz := info.Size(pb)
-	// make sure buf has enough capacity
-	if newCap := len(p.buf) + siz; newCap > cap(p.buf) {
-		p.buf = append(make([]byte, 0, newCap), p.buf...)
-	}
+	p.grow(siz) // make sure buf has enough capacity
 	p.buf, err = info.Marshal(p.buf, pb, p.deterministic)
 	return err
 }
+
+// grow grows the buffer's capacity, if necessary, to guarantee space for
+// another n bytes. After grow(n), at least n bytes can be written to the
+// buffer without another allocation.
+func (p *Buffer) grow(n int) {
+	need := len(p.buf) + n
+	if need <= cap(p.buf) {
+		return
+	}
+	newCap := len(p.buf) * 2
+	if newCap < need {
+		newCap = need
+	}
+	p.buf = append(make([]byte, 0, newCap), p.buf...)
+}