11package gitdiff
22
33import (
4+ "bytes"
45 "errors"
56 "fmt"
67 "io"
@@ -69,10 +70,7 @@ func applyError(err error, args ...interface{}) error {
6970
7071 e , ok := err .(* ApplyError )
7172 if ! ok {
72- if err == io .EOF {
73- err = io .ErrUnexpectedEOF
74- }
75- e = & ApplyError {err : err }
73+ e = & ApplyError {err : wrapEOF (err )}
7674 }
7775 for _ , arg := range args {
7876 switch v := arg .(type ) {
@@ -95,10 +93,14 @@ func applyError(err error, args ...interface{}) error {
9593// Partial data may be written to dst in this case.
9694func (f * File ) ApplyStrict (dst io.Writer , src io.Reader ) error {
9795 if f .IsBinary {
96+ data , err := ioutil .ReadAll (src )
97+ if err != nil {
98+ return applyError (err )
99+ }
98100 if f .BinaryFragment != nil {
99- return f .BinaryFragment .Apply (dst , src )
101+ return f .BinaryFragment .Apply (dst , bytes . NewReader ( data ) )
100102 }
101- _ , err := io . Copy ( dst , src )
103+ _ , err = dst . Write ( data )
102104 return applyError (err )
103105 }
104106
@@ -196,10 +198,7 @@ func copyLines(dst io.Writer, src LineReader, limit int64) (string, int64, error
196198 }
197199 return "" , n , & Conflict {"fragment overlaps with an applied fragment" }
198200 case err != nil :
199- if err == io .EOF {
200- err = io .ErrUnexpectedEOF
201- }
202- return line , n , err
201+ return line , n , wrapEOF (err )
203202 }
204203
205204 if _ , err := io .WriteString (dst , line ); err != nil {
@@ -213,19 +212,14 @@ func copyLines(dst io.Writer, src LineReader, limit int64) (string, int64, error
213212//
214213// Unlike text fragments, binary fragments do not distinguish between strict
215214// and non-strict application.
216- func (f * BinaryFragment ) Apply (dst io.Writer , src io.Reader ) error {
217- fullSrc , err := ioutil .ReadAll (src )
218- if err != nil {
219- return err
220- }
221-
215+ func (f * BinaryFragment ) Apply (dst io.Writer , src io.ReaderAt ) error {
222216 switch f .Method {
223217 case BinaryPatchLiteral :
224218 if _ , err := dst .Write (f .Data ); err != nil {
225219 return applyError (err )
226220 }
227221 case BinaryPatchDelta :
228- if err := applyBinaryDeltaFragment (dst , fullSrc , f .Data ); err != nil {
222+ if err := applyBinaryDeltaFragment (dst , src , f .Data ); err != nil {
229223 return applyError (err )
230224 }
231225 default :
@@ -235,10 +229,10 @@ func (f *BinaryFragment) Apply(dst io.Writer, src io.Reader) error {
235229 return nil
236230}
237231
238- func applyBinaryDeltaFragment (dst io.Writer , src , frag []byte ) error {
232+ func applyBinaryDeltaFragment (dst io.Writer , src io. ReaderAt , frag []byte ) error {
239233 srcSize , delta := readBinaryDeltaSize (frag )
240- if srcSize != int64 ( len ( src )) {
241- return & Conflict { "fragment src size does not match actual src size" }
234+ if err := checkBinarySrcSize ( srcSize , src ); err != nil {
235+ return err
242236 }
243237
244238 dstSize , delta := readBinaryDeltaSize (delta )
@@ -314,7 +308,7 @@ func applyBinaryDeltaAdd(w io.Writer, op byte, delta []byte) (n int64, rest []by
314308// size bytes are present in little-endian order: if bit 0 is set, offset1 is
315309// present, etc. If no offset or size bytes are present, offset is 0 and size
316310// is 0x10000. See also pack-format.txt in the Git source.
317- func applyBinaryDeltaCopy (w io.Writer , op byte , delta , src []byte ) (n int64 , rest []byte , err error ) {
311+ func applyBinaryDeltaCopy (w io.Writer , op byte , delta []byte , src io. ReaderAt ) (n int64 , rest []byte , err error ) {
318312 const defaultSize = 0x10000
319313
320314 unpack := func (start , bits uint ) (v int64 ) {
@@ -341,6 +335,35 @@ func applyBinaryDeltaCopy(w io.Writer, op byte, delta, src []byte) (n int64, res
341335 size = defaultSize
342336 }
343337
344- _ , err = w .Write (src [offset : offset + size ])
338+ // TODO(bkeyes): consider pooling these buffers
339+ b := make ([]byte , size )
340+ if _ , err := src .ReadAt (b , offset ); err != nil {
341+ return 0 , delta , wrapEOF (err )
342+ }
343+
344+ _ , err = w .Write (b )
345345 return size , delta , err
346346}
347+
348+ func checkBinarySrcSize (size int64 , src io.ReaderAt ) error {
349+ start := size
350+ if start > 0 {
351+ start --
352+ }
353+ var b [2 ]byte
354+ n , err := src .ReadAt (b [:], start )
355+ if err == io .EOF && (size == 0 && n == 0 ) || (size > 0 && n == 1 ) {
356+ return nil
357+ }
358+ if err != nil && err != io .EOF {
359+ return err
360+ }
361+ return & Conflict {"fragment src size does not match actual src size" }
362+ }
363+
364+ func wrapEOF (err error ) error {
365+ if err == io .EOF {
366+ err = io .ErrUnexpectedEOF
367+ }
368+ return err
369+ }
0 commit comments