1
1
package gitdiff
2
2
3
3
import (
4
+ "bytes"
4
5
"errors"
5
6
"fmt"
6
7
"io"
@@ -69,10 +70,7 @@ func applyError(err error, args ...interface{}) error {
69
70
70
71
e , ok := err .(* ApplyError )
71
72
if ! ok {
72
- if err == io .EOF {
73
- err = io .ErrUnexpectedEOF
74
- }
75
- e = & ApplyError {err : err }
73
+ e = & ApplyError {err : wrapEOF (err )}
76
74
}
77
75
for _ , arg := range args {
78
76
switch v := arg .(type ) {
@@ -95,10 +93,14 @@ func applyError(err error, args ...interface{}) error {
95
93
// Partial data may be written to dst in this case.
96
94
func (f * File ) ApplyStrict (dst io.Writer , src io.Reader ) error {
97
95
if f .IsBinary {
96
+ data , err := ioutil .ReadAll (src )
97
+ if err != nil {
98
+ return applyError (err )
99
+ }
98
100
if f .BinaryFragment != nil {
99
- return f .BinaryFragment .Apply (dst , src )
101
+ return f .BinaryFragment .Apply (dst , bytes . NewReader ( data ) )
100
102
}
101
- _ , err := io . Copy ( dst , src )
103
+ _ , err = dst . Write ( data )
102
104
return applyError (err )
103
105
}
104
106
@@ -196,10 +198,7 @@ func copyLines(dst io.Writer, src LineReader, limit int64) (string, int64, error
196
198
}
197
199
return "" , n , & Conflict {"fragment overlaps with an applied fragment" }
198
200
case err != nil :
199
- if err == io .EOF {
200
- err = io .ErrUnexpectedEOF
201
- }
202
- return line , n , err
201
+ return line , n , wrapEOF (err )
203
202
}
204
203
205
204
if _ , err := io .WriteString (dst , line ); err != nil {
@@ -213,19 +212,14 @@ func copyLines(dst io.Writer, src LineReader, limit int64) (string, int64, error
213
212
//
214
213
// Unlike text fragments, binary fragments do not distinguish between strict
215
214
// 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 {
222
216
switch f .Method {
223
217
case BinaryPatchLiteral :
224
218
if _ , err := dst .Write (f .Data ); err != nil {
225
219
return applyError (err )
226
220
}
227
221
case BinaryPatchDelta :
228
- if err := applyBinaryDeltaFragment (dst , fullSrc , f .Data ); err != nil {
222
+ if err := applyBinaryDeltaFragment (dst , src , f .Data ); err != nil {
229
223
return applyError (err )
230
224
}
231
225
default :
@@ -235,10 +229,10 @@ func (f *BinaryFragment) Apply(dst io.Writer, src io.Reader) error {
235
229
return nil
236
230
}
237
231
238
- func applyBinaryDeltaFragment (dst io.Writer , src , frag []byte ) error {
232
+ func applyBinaryDeltaFragment (dst io.Writer , src io. ReaderAt , frag []byte ) error {
239
233
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
242
236
}
243
237
244
238
dstSize , delta := readBinaryDeltaSize (delta )
@@ -314,7 +308,7 @@ func applyBinaryDeltaAdd(w io.Writer, op byte, delta []byte) (n int64, rest []by
314
308
// size bytes are present in little-endian order: if bit 0 is set, offset1 is
315
309
// present, etc. If no offset or size bytes are present, offset is 0 and size
316
310
// 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 ) {
318
312
const defaultSize = 0x10000
319
313
320
314
unpack := func (start , bits uint ) (v int64 ) {
@@ -341,6 +335,35 @@ func applyBinaryDeltaCopy(w io.Writer, op byte, delta, src []byte) (n int64, res
341
335
size = defaultSize
342
336
}
343
337
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 )
345
345
return size , delta , err
346
346
}
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