this repo has no description
0
fork

Configure Feed

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

Implement binary fragment application

Currently untested, but based on code I validated against some generated
patches. Tests comming in a future commit.

+126 -1
+126 -1
gitdiff/apply.go
··· 1 1 package gitdiff 2 2 3 3 import ( 4 + "errors" 4 5 "fmt" 5 6 "io" 7 + "io/ioutil" 6 8 ) 7 9 8 10 // Conflict indicates an apply failed due to a conflict between the patch and ··· 212 214 // Unlike text fragments, binary fragments do not distinguish between strict 213 215 // and non-strict application. 214 216 func (f *BinaryFragment) Apply(dst io.Writer, src io.Reader) error { 215 - panic("TODO(bkeyes): unimplemented") 217 + fullSrc, err := ioutil.ReadAll(src) 218 + if err != nil { 219 + return err 220 + } 221 + 222 + switch f.Method { 223 + case BinaryPatchLiteral: 224 + if _, err := dst.Write(f.Data); err != nil { 225 + return applyError(err) 226 + } 227 + case BinaryPatchDelta: 228 + if err := applyBinaryDeltaFragment(dst, fullSrc, f.Data); err != nil { 229 + return applyError(err) 230 + } 231 + } 232 + return applyError(fmt.Errorf("unsupported binary patch method: %v", f.Method)) 233 + } 234 + 235 + func applyBinaryDeltaFragment(dst io.Writer, src, frag []byte) error { 236 + srcSize, delta := readBinaryDeltaSize(frag) 237 + if srcSize != int64(len(src)) { 238 + return &Conflict{"fragment src size does not match actual src size"} 239 + } 240 + 241 + dstSize, delta := readBinaryDeltaSize(delta) 242 + 243 + for len(delta) > 0 { 244 + op := delta[0] 245 + if op == 0 { 246 + return errors.New("invalid delta opcode 0") 247 + } 248 + 249 + var n int64 250 + var err error 251 + switch op & 0x80 { 252 + case 0x80: 253 + n, delta, err = applyBinaryDeltaCopy(dst, op, delta[1:], src) 254 + case 0x00: 255 + n, delta, err = applyBinaryDeltaAdd(dst, op, delta[1:]) 256 + } 257 + if err != nil { 258 + return err 259 + } 260 + dstSize -= n 261 + } 262 + 263 + if dstSize != 0 { 264 + return errors.New("corrupt binary delta: insufficient or extra data") 265 + } 266 + return nil 267 + } 268 + 269 + // readBinaryDeltaSize reads a variable length size from a delta-encoded binary 270 + // fragment, returing the size and the unused data. Data is encoded as: 271 + // 272 + // [[1xxxxxxx]...] [0xxxxxxx] 273 + // 274 + // in little-endian order, with 7 bits of the value per byte. 275 + func readBinaryDeltaSize(d []byte) (size int64, rest []byte) { 276 + shift := uint(0) 277 + for i, b := range d { 278 + size |= int64(b&0x7F) << shift 279 + shift += 7 280 + if b <= 0x7F { 281 + return size, d[i+1:] 282 + } 283 + } 284 + return size, nil 285 + } 286 + 287 + // applyBinaryDeltaAdd applies an add opcode in a delta-encoded binary 288 + // fragment, returning the amount of data written and the usused part of the 289 + // fragment. An add operation takes the form: 290 + // 291 + // [0xxxxxx][[data1]...] 292 + // 293 + // where the lower seven bits of the opcode is the number of data bytes 294 + // following the opcode. See also pack-format.txt in the Git source. 295 + func applyBinaryDeltaAdd(w io.Writer, op byte, delta []byte) (n int64, rest []byte, err error) { 296 + size := int(op) 297 + if len(delta) < size { 298 + return 0, delta, errors.New("corrupt binary delta: incomplete add") 299 + } 300 + _, err = w.Write(delta[:size]) 301 + return int64(size), delta[size:], err 302 + } 303 + 304 + // applyBinaryDeltaCopy applies a copy opcode in a delta-encoded binary 305 + // fragment, returing the amount of data written and the unused part of the 306 + // fragment. A copy operation takes the form: 307 + // 308 + // [1xxxxxxx][offset1][offset2][offset3][offset4][size1][size2][size3] 309 + // 310 + // where the lower seven bits of the opcode determine which non-zero offset and 311 + // size bytes are present in little-endian order: if bit 0 is set, offset1 is 312 + // present, etc. If no offset or size bytes are present, offset is 0 and size 313 + // is 0x10000. See also pack-format.txt in the Git source. 314 + func applyBinaryDeltaCopy(w io.Writer, op byte, delta, src []byte) (n int64, rest []byte, err error) { 315 + unpack := func(start, bits uint) (v int64) { 316 + for i := uint(0); i < bits; i++ { 317 + mask := byte(1 << (i + start)) 318 + if op&mask > 0 { 319 + if len(delta) == 0 { 320 + err = errors.New("corrupt binary delta: incomplete copy") 321 + return 322 + } 323 + v |= int64(delta[0]) << (8 * i) 324 + delta = delta[1:] 325 + } 326 + } 327 + return 328 + } 329 + 330 + offset := unpack(0, 4) 331 + size := unpack(4, 3) 332 + if err != nil { 333 + return 0, delta, err 334 + } 335 + if size == 0 { 336 + size = 0x10000 337 + } 338 + 339 + _, err = w.Write(src[offset : offset+size]) 340 + return size, delta, err 216 341 }