Merge pull request #578 from erizocosmico/perf/reduce-gc-press

packfile: improve performance a little by reducing gc pressure
diff --git a/plumbing/format/packfile/common.go b/plumbing/format/packfile/common.go
index 728cb16..7dad1f6 100644
--- a/plumbing/format/packfile/common.go
+++ b/plumbing/format/packfile/common.go
@@ -1,7 +1,9 @@
 package packfile
 
 import (
+	"bytes"
 	"io"
+	"sync"
 
 	"gopkg.in/src-d/go-git.v4/plumbing/storer"
 	"gopkg.in/src-d/go-git.v4/utils/ioutil"
@@ -49,3 +51,9 @@
 	_, err = io.Copy(w, packfile)
 	return err
 }
+
+var bufPool = sync.Pool{
+	New: func() interface{} {
+		return bytes.NewBuffer(nil)
+	},
+}
diff --git a/plumbing/format/packfile/decoder.go b/plumbing/format/packfile/decoder.go
index e49de51..3d475b2 100644
--- a/plumbing/format/packfile/decoder.go
+++ b/plumbing/format/packfile/decoder.go
@@ -347,7 +347,8 @@
 }
 
 func (d *Decoder) fillREFDeltaObjectContent(obj plumbing.EncodedObject, ref plumbing.Hash) (uint32, error) {
-	buf := bytes.NewBuffer(nil)
+	buf := bufPool.Get().(*bytes.Buffer)
+	buf.Reset()
 	_, crc, err := d.s.NextObject(buf)
 	if err != nil {
 		return 0, err
@@ -364,6 +365,7 @@
 	obj.SetType(base.Type())
 	err = ApplyDelta(obj, base, buf.Bytes())
 	d.cachePut(obj)
+	bufPool.Put(buf)
 
 	return crc, err
 }
diff --git a/plumbing/format/packfile/diff_delta.go b/plumbing/format/packfile/diff_delta.go
index 60a04d9..7e9f822 100644
--- a/plumbing/format/packfile/diff_delta.go
+++ b/plumbing/format/packfile/diff_delta.go
@@ -60,13 +60,15 @@
 
 // DiffDelta returns the delta that transforms src into tgt.
 func DiffDelta(src []byte, tgt []byte) []byte {
-	buf := bytes.NewBuffer(nil)
+	buf := bufPool.Get().(*bytes.Buffer)
+	buf.Reset()
 	buf.Write(deltaEncodeSize(len(src)))
 	buf.Write(deltaEncodeSize(len(tgt)))
 
 	sindex := initMatch(src)
 
-	ibuf := bytes.NewBuffer(nil)
+	ibuf := bufPool.Get().(*bytes.Buffer)
+	ibuf.Reset()
 	for i := 0; i < len(tgt); i++ {
 		offset, l := findMatch(src, tgt, sindex, i)
 
@@ -93,8 +95,12 @@
 	}
 
 	encodeInsertOperation(ibuf, buf)
+	bytes := buf.Bytes()
 
-	return buf.Bytes()
+	bufPool.Put(buf)
+	bufPool.Put(ibuf)
+
+	return bytes
 }
 
 func encodeInsertOperation(ibuf, buf *bytes.Buffer) {
diff --git a/plumbing/format/packfile/scanner.go b/plumbing/format/packfile/scanner.go
index 1dab2f2..d2d776f 100644
--- a/plumbing/format/packfile/scanner.go
+++ b/plumbing/format/packfile/scanner.go
@@ -9,6 +9,7 @@
 	"hash/crc32"
 	"io"
 	stdioutil "io/ioutil"
+	"sync"
 
 	"gopkg.in/src-d/go-git.v4/plumbing"
 	"gopkg.in/src-d/go-git.v4/utils/binary"
@@ -291,10 +292,18 @@
 	}
 
 	defer ioutil.CheckClose(s.zr, &err)
-	n, err = io.Copy(w, s.zr)
+	buf := byteSlicePool.Get().([]byte)
+	n, err = io.CopyBuffer(w, s.zr, buf)
+	byteSlicePool.Put(buf)
 	return
 }
 
+var byteSlicePool = sync.Pool{
+	New: func() interface{} {
+		return make([]byte, 32*1024)
+	},
+}
+
 // SeekFromStart sets a new offset from start, returns the old position before
 // the change.
 func (s *Scanner) SeekFromStart(offset int64) (previous int64, err error) {
@@ -324,7 +333,9 @@
 
 // Close reads the reader until io.EOF
 func (s *Scanner) Close() error {
-	_, err := io.Copy(stdioutil.Discard, s.r)
+	buf := byteSlicePool.Get().([]byte)
+	_, err := io.CopyBuffer(stdioutil.Discard, s.r, buf)
+	byteSlicePool.Put(buf)
 	return err
 }