func (plan *PatchPlan) appendFilePlan(srcFile *fs.File, dstPath string) error { match, err := MatchIndex(plan.srcStore.Index(), plan.dstStore.Resolve(dstPath)) if match == nil { return err } match.SrcSize = srcFile.Size // Create a local temporary file in which to effect changes localTemp := &LocalTemp{ Path: &LocalPath{ LocalStore: plan.dstStore, RelPath: dstPath}, Size: match.SrcSize} plan.Cmds = append(plan.Cmds, localTemp) for _, blockMatch := range match.BlockMatches { plan.Cmds = append(plan.Cmds, &LocalTempCopy{ Temp: localTemp, LocalOffset: blockMatch.SrcBlock.Offset(), TempOffset: blockMatch.DstOffset, Length: int64(fs.BLOCKSIZE)}) } for _, srcRange := range match.NotMatched() { plan.Cmds = append(plan.Cmds, &SrcTempCopy{ Temp: localTemp, SrcStrong: srcFile.Strong(), SrcOffset: srcRange.From, TempOffset: srcRange.From, Length: srcRange.To - srcRange.From}) } // Replace dst file with temp plan.Cmds = append(plan.Cmds, &ReplaceWithTemp{Temp: localTemp}) return nil }
func MatchFile(srcFile fs.File, dst string) (match *FileMatch, err os.Error) { match = &FileMatch{SrcSize: srcFile.Info().Size} var dstOffset int64 dstF, err := os.Open(dst) if dstF == nil { return nil, err } defer dstF.Close() if dstInfo, err := dstF.Stat(); dstInfo == nil { return nil, err } else if !dstInfo.IsRegular() { return nil, os.NewError(fmt.Sprintf("%s: not a regular file", dst)) } else { match.DstSize = dstInfo.Size } dstWeak := new(fs.WeakChecksum) var buf [fs.BLOCKSIZE]byte var sbuf [1]byte var window []byte // Scan a block, // then roll checksum a byte at a time until match or eof // repeat above until eof SCAN: for { switch rd, err := dstF.Read(buf[:]); true { case rd < 0: return nil, err case rd == 0: break SCAN case rd > 0: blocksize := rd dstOffset += int64(rd) window = buf[:rd] dstWeak.Reset() dstWeak.Write(window[:]) for { // Check for a weak checksum match if matchBlock, has := srcFile.Repo().WeakBlock(dstWeak.Get()); has { // Double-check with the strong checksum if fs.StrongChecksum(window[:blocksize]) == matchBlock.Info().Strong { // We've got a block match in dest match.BlockMatches = append(match.BlockMatches, &BlockMatch{ SrcBlock: matchBlock, DstOffset: dstOffset - int64(blocksize)}) break } } // Read the next byte switch srd, err := dstF.Read(sbuf[:]); true { case srd < 0: return nil, err case srd == 0: break SCAN case srd == 1: dstOffset++ // Roll the weak checksum & the buffer dstWeak.Roll(window[0], sbuf[0]) window = append(window[1:], sbuf[0]) break case srd > 1: return nil, os.NewError("Internal read error trying advance one byte.") } } } } return match, nil }