this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Use io.ReaderAt to implement binary applies

This better matches the actual contract of the delta patch, which reads
fixed size chunks of the source files at arbitrary positions.

+45 -22
+45 -22
gitdiff/apply.go
··· 1 1 package gitdiff 2 2 3 3 import ( 4 + "bytes" 4 5 "errors" 5 6 "fmt" 6 7 "io" ··· 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 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 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 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 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 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 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 + }