func (plan *PatchPlan) appendFilePlan(srcFile fs.File, dstPath string) os.Error { match, err := MatchFile(srcFile, plan.dstStore.Resolve(dstPath)) if match == nil { return err } match.SrcSize = srcFile.Info().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 { // TODO: math/imath length := srcFile.Info().Size - blockMatch.SrcBlock.Info().Offset() if length > int64(fs.BLOCKSIZE) { length = int64(fs.BLOCKSIZE) } plan.Cmds = append(plan.Cmds, &LocalTempCopy{ Temp: localTemp, LocalOffset: blockMatch.SrcBlock.Info().Offset(), TempOffset: blockMatch.DstOffset, Length: length}) } for _, srcRange := range match.NotMatched() { plan.Cmds = append(plan.Cmds, &SrcTempCopy{ Temp: localTemp, SrcStrong: srcFile.Info().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 }