11package gitdiff
22
33import (
4- "bytes"
54 "errors"
65 "fmt"
76 "io"
8- "io/ioutil"
97)
108
119// Conflict indicates an apply failed due to a conflict between the patch and
@@ -85,70 +83,101 @@ func applyError(err error, args ...interface{}) error {
8583 return e
8684}
8785
88- // ApplyStrict writes data from src to dst, modifying it as described by the
89- // fragments in the file. For text files, each fragment, including all context
90- // lines, must exactly match src at the expected line number.
86+ var (
87+ errApplyInProgress = errors .New ("gitdiff: incompatible apply in progress" )
88+ )
89+
90+ const (
91+ applyInitial = iota
92+ applyText
93+ applyBinary
94+ )
95+
96+ // Applier applies changes described in fragments to source data. If changes
97+ // are described in multiple fragments, those fragments must be applied in
98+ // order, usually by calling ApplyFile.
9199//
92- // If the apply fails, ApplyStrict returns an *ApplyError wrapping the cause.
93- // Partial data may be written to dst in this case.
94- func (f * File ) ApplyStrict (dst io.Writer , src io.Reader ) error {
95- // TODO(bkeyes): take an io.ReaderAt and avoid this!
96- data , err := ioutil .ReadAll (src )
97- if err != nil {
98- return applyError (err )
99- }
100+ // By default, Applier operates in "strict" mode, where fragment content and
101+ // positions must exactly match those of the source.
102+ //
103+ // If an error occurs while applying, methods on Applier return instances of
104+ // *ApplyError that annotate the wrapped error with additional information
105+ // when available. If the error is because of a conflict between a fragment and
106+ // the source, the wrapped error will be a *Conflict.
107+ //
108+ // While an Applier can apply both text and binary fragments, only one fragment
109+ // type can be used without resetting the Applier. The first fragment applied
110+ // sets the type for the Applier. Mixing fragment types or mixing
111+ // fragment-level and file-level applies results in an error.
112+ type Applier struct {
113+ src io.ReaderAt
114+ lineSrc LineReaderAt
115+ nextLine int64
116+ applyType int
117+ }
100118
101- if f .IsBinary {
102- if f .BinaryFragment != nil {
103- return f .BinaryFragment .Apply (dst , bytes .NewReader (data ))
119+ // NewApplier creates an Applier that reads data from src. If src is a
120+ // LineReaderAt, it is used directly to apply text fragments.
121+ func NewApplier (src io.ReaderAt ) * Applier {
122+ a := new (Applier )
123+ a .Reset (src )
124+ return a
125+ }
126+
127+ // Reset resets the input and internal state of the Applier. If src is nil, the
128+ // existing source is reused.
129+ func (a * Applier ) Reset (src io.ReaderAt ) {
130+ if src != nil {
131+ a .src = src
132+ if lineSrc , ok := src .(LineReaderAt ); ok {
133+ a .lineSrc = lineSrc
134+ } else {
135+ a .lineSrc = & lineReaderAt {r : src }
104136 }
105- _ , err = dst .Write (data )
106- return applyError (err )
137+ }
138+ a .nextLine = 0
139+ a .applyType = applyInitial
140+ }
141+
142+ // ApplyFile applies the changes in all of the fragments of f and writes the
143+ // result to dst.
144+ func (a * Applier ) ApplyFile (dst io.Writer , f * File ) error {
145+ if a .applyType != applyInitial {
146+ return applyError (errApplyInProgress )
107147 }
108148
109- // TODO(bkeyes): check for this conflict case
110- // &Conflict{"cannot create new file from non-empty src"}
149+ if f .IsBinary && f .BinaryFragment != nil {
150+ return a .ApplyBinaryFragment (dst , f .BinaryFragment )
151+ }
111152
112- lra := NewLineReaderAt (bytes .NewReader (data ))
153+ // TODO(bkeyes): sort fragments by start position
154+ // TODO(bkeyes): merge overlapping fragments
113155
114- var next int64
115156 for i , frag := range f .TextFragments {
116- next , err = frag .ApplyStrict (dst , lra , next )
117- if err != nil {
157+ if err := a .ApplyTextFragment (dst , frag ); err != nil {
118158 return applyError (err , fragNum (i ))
119159 }
120160 }
121161
122- // TODO(bkeyes): extract this to a utility
123- buf := make ([][]byte , 64 )
124- for {
125- n , err := lra .ReadLinesAt (buf , next )
126- if err != nil && err != io .EOF {
127- return applyError (err , lineNum (next + int64 (n )))
128- }
129-
130- for i := 0 ; i < n ; i ++ {
131- if _ , err := dst .Write (buf [n ]); err != nil {
132- return applyError (err , lineNum (next + int64 (n )))
133- }
134- }
162+ return applyError (a .Flush (dst ))
163+ }
135164
136- next += int64 (n )
137- if n < len (buf ) {
138- return nil
139- }
165+ // ApplyTextFragment applies the changes in the fragment f and writes unwritten
166+ // data before the start of the fragment and the result to dst. If multiple
167+ // text fragments apply to the same source, ApplyTextFragment must be called in
168+ // order of increasing start position. As a result, each fragment can be
169+ // applied at most once before a call to Reset.
170+ func (a * Applier ) ApplyTextFragment (dst io.Writer , f * TextFragment ) error {
171+ switch a .applyType {
172+ case applyInitial , applyText :
173+ default :
174+ return applyError (errApplyInProgress )
140175 }
141- }
176+ a . applyType = applyText
142177
143- // ApplyStrict copies from src to dst, from line start through then end of the
144- // fragment, modifying the data as described by the fragment. The fragment,
145- // including all context lines, must exactly match src at the expected line
146- // number. ApplyStrict returns the number of the next unprocessed line in src
147- // and any error. When the error is not non-nil, partial data may be written.
148- func (f * TextFragment ) ApplyStrict (dst io.Writer , src LineReaderAt , start int64 ) (next int64 , err error ) {
149178 // application code assumes fragment fields are consistent
150179 if err := f .Validate (); err != nil {
151- return start , applyError (err )
180+ return applyError (err )
152181 }
153182
154183 // lines are 0-indexed, positions are 1-indexed (but new files have position = 0)
@@ -158,24 +187,35 @@ func (f *TextFragment) ApplyStrict(dst io.Writer, src LineReaderAt, start int64)
158187 }
159188 fragEnd := fragStart + f .OldLines
160189
190+ start := a .nextLine
161191 if fragStart < start {
162- return start , applyError (& Conflict {"fragment overlaps with an applied fragment" })
192+ return applyError (& Conflict {"fragment overlaps with an applied fragment" })
193+ }
194+
195+ if f .OldPosition == 0 {
196+ ok , err := isLen (a .src , 0 )
197+ if err != nil {
198+ return applyError (err )
199+ }
200+ if ! ok {
201+ return applyError (& Conflict {"cannot create new file from non-empty src" })
202+ }
163203 }
164204
165205 preimage := make ([][]byte , fragEnd - start )
166- n , err := src .ReadLinesAt (preimage , start )
206+ n , err := a . lineSrc .ReadLinesAt (preimage , start )
167207 switch {
168208 case err == nil :
169209 case err == io .EOF && n == len (preimage ): // last line of frag has no newline character
170210 default :
171- return start , applyError (err , lineNum (start + int64 (n )))
211+ return applyError (err , lineNum (start + int64 (n )))
172212 }
173213
174214 // copy leading data before the fragment starts
175215 for i , line := range preimage [:fragStart - start ] {
176216 if _ , err := dst .Write (line ); err != nil {
177- next = start + int64 (i )
178- return next , applyError (err , lineNum (next ))
217+ a . nextLine = start + int64 (i )
218+ return applyError (err , lineNum (a . nextLine ))
179219 }
180220 }
181221 preimage = preimage [fragStart - start :]
@@ -184,14 +224,15 @@ func (f *TextFragment) ApplyStrict(dst io.Writer, src LineReaderAt, start int64)
184224 used := int64 (0 )
185225 for i , line := range f .Lines {
186226 if err := applyTextLine (dst , line , preimage , used ); err != nil {
187- next = fragStart + used
188- return next , applyError (err , lineNum (next ), fragLineNum (i ))
227+ a . nextLine = fragStart + used
228+ return applyError (err , lineNum (a . nextLine ), fragLineNum (i ))
189229 }
190230 if line .Old () {
191231 used ++
192232 }
193233 }
194- return fragStart + used , nil
234+ a .nextLine = fragStart + used
235+ return nil
195236}
196237
197238func applyTextLine (dst io.Writer , line Line , preimage [][]byte , i int64 ) (err error ) {
@@ -201,34 +242,53 @@ func applyTextLine(dst io.Writer, line Line, preimage [][]byte, i int64) (err er
201242 if line .New () {
202243 _ , err = io .WriteString (dst , line .Line )
203244 }
204- return
245+ return err
205246}
206247
207- // Apply writes data from src to dst, modifying it as described by the
208- // fragment.
209- //
210- // Unlike text fragments, binary fragments do not distinguish between strict
211- // and non-strict application.
212- func (f * BinaryFragment ) Apply (dst io.Writer , src io.ReaderAt ) error {
248+ // Flush writes any data following the last applied fragment to dst.
249+ func (a * Applier ) Flush (dst io.Writer ) (err error ) {
250+ switch a .applyType {
251+ case applyInitial :
252+ _ , err = copyFrom (dst , a .src , 0 )
253+ case applyText :
254+ _ , err = copyLinesFrom (dst , a .lineSrc , a .nextLine )
255+ case applyBinary :
256+ // nothing to flush, binary apply "consumes" full source
257+ }
258+ return err
259+ }
260+
261+ // ApplyBinaryFragment applies the changes in the fragment f and writes the
262+ // result to dst. At most one binary fragment can be applied before a call to
263+ // Reset.
264+ func (a * Applier ) ApplyBinaryFragment (dst io.Writer , f * BinaryFragment ) error {
265+ if a .applyType != applyInitial {
266+ return applyError (errApplyInProgress )
267+ }
268+ a .applyType = applyText
269+
270+ if f == nil {
271+ return applyError (errors .New ("nil fragment" ))
272+ }
273+
213274 switch f .Method {
214275 case BinaryPatchLiteral :
215276 if _ , err := dst .Write (f .Data ); err != nil {
216277 return applyError (err )
217278 }
218279 case BinaryPatchDelta :
219- if err := applyBinaryDeltaFragment (dst , src , f .Data ); err != nil {
280+ if err := applyBinaryDeltaFragment (dst , a . src , f .Data ); err != nil {
220281 return applyError (err )
221282 }
222283 default :
223284 return applyError (fmt .Errorf ("unsupported binary patch method: %v" , f .Method ))
224285 }
225-
226286 return nil
227287}
228288
229289func applyBinaryDeltaFragment (dst io.Writer , src io.ReaderAt , frag []byte ) error {
230290 srcSize , delta := readBinaryDeltaSize (frag )
231- if err := checkBinarySrcSize (srcSize , src ); err != nil {
291+ if err := checkBinarySrcSize (src , srcSize ); err != nil {
232292 return err
233293 }
234294
@@ -342,20 +402,15 @@ func applyBinaryDeltaCopy(w io.Writer, op byte, delta []byte, src io.ReaderAt) (
342402 return size , delta , err
343403}
344404
345- func checkBinarySrcSize (size int64 , src io.ReaderAt ) error {
346- start := size
347- if start > 0 {
348- start --
349- }
350- var b [2 ]byte
351- n , err := src .ReadAt (b [:], start )
352- if err == io .EOF && (size == 0 && n == 0 ) || (size > 0 && n == 1 ) {
353- return nil
354- }
355- if err != nil && err != io .EOF {
405+ func checkBinarySrcSize (r io.ReaderAt , size int64 ) error {
406+ ok , err := isLen (r , size )
407+ if err != nil {
356408 return err
357409 }
358- return & Conflict {"fragment src size does not match actual src size" }
410+ if ! ok {
411+ return & Conflict {"fragment src size does not match actual src size" }
412+ }
413+ return nil
359414}
360415
361416func wrapEOF (err error ) error {
0 commit comments