webdav: make memFile.Write allocate less often.

benchmark                 old ns/op     new ns/op     delta
BenchmarkMemFileWrite     8498028       625563        -92.64%

Change-Id: Iec7dd3931c891c9d6d9d5c6ccd05300b031a5f86
diff --git a/webdav/file_test.go b/webdav/file_test.go
index ff68c4a..d95d5a8 100644
--- a/webdav/file_test.go
+++ b/webdav/file_test.go
@@ -252,9 +252,12 @@
 		"write C",
 		"wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........C",
 		"wantSize 41",
-		"seek set 43 want 43",
 		"write D",
-		"wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........C..D",
+		"wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD",
+		"wantSize 42",
+		"seek set 43 want 43",
+		"write E",
+		"wantData abcdEFghijkyyyzzstsZZAAAABBBBB..........CD.E",
 		"wantSize 44",
 		"seek set 0 want 0",
 		"write 5*123456789_",
@@ -396,3 +399,54 @@
 		}
 	}
 }
+
+// TestMemFileWriteAllocs tests that writing N consecutive 1KiB chunks to a
+// memFile doesn't allocate a new buffer for each of those N times. Otherwise,
+// calling io.Copy(aMemFile, src) is likely to have quadratic complexity.
+func TestMemFileWriteAllocs(t *testing.T) {
+	fs := NewMemFS()
+	f, err := fs.OpenFile("/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+	if err != nil {
+		t.Fatalf("OpenFile: %v", err)
+	}
+	defer f.Close()
+
+	xxx := make([]byte, 1024)
+	for i := range xxx {
+		xxx[i] = 'x'
+	}
+
+	a := testing.AllocsPerRun(100, func() {
+		f.Write(xxx)
+	})
+	// AllocsPerRun returns an integral value, so we compare the rounded-down
+	// number to zero.
+	if a > 0 {
+		t.Fatalf("%v allocs per run, want 0", a)
+	}
+}
+
+func BenchmarkMemFileWrite(b *testing.B) {
+	fs := NewMemFS()
+	xxx := make([]byte, 1024)
+	for i := range xxx {
+		xxx[i] = 'x'
+	}
+
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		f, err := fs.OpenFile("/xxx", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
+		if err != nil {
+			b.Fatalf("OpenFile: %v", err)
+		}
+		for j := 0; j < 100; j++ {
+			f.Write(xxx)
+		}
+		if err := f.Close(); err != nil {
+			b.Fatalf("Close: %v", err)
+		}
+		if err := fs.RemoveAll("/xxx"); err != nil {
+			b.Fatalf("RemoveAll: %v", err)
+		}
+	}
+}