Ticket #45: BackupFileDiff.patch

File BackupFileDiff.patch, 46.7 KB (added by aharper, 10 months ago)
  • lib/backupclient/BackupStoreFile.h

     
    206206        static void ResetStats(); 
    207207        static BackupStoreFileStats msStats; 
    208208         
    209         // For debug 
    210 #ifndef NDEBUG 
    211         static bool TraceDetailsOfDiffProcess; 
    212 #endif 
    213  
    214209        // For decoding encoded files 
    215210        static void DumpFile(void *clibFileHandle, bool ToTrace, IOStream &rFile); 
    216211}; 
  • test/backupdiff/testbackupdiff.cpp

     
    376376        // Want to trace out all the details 
    377377        #ifndef NDEBUG 
    378378        #ifndef WIN32 
    379         BackupStoreFile::TraceDetailsOfDiffProcess = true; 
     379        Logging::SetGlobalLevel(Log::TRACE); 
    380380        #endif 
    381381        #endif 
    382382 
  • lib/backupclient/BackupStoreFileDiff.cpp

     
    3636using namespace BackupStoreFileCryptVar; 
    3737using namespace BackupStoreFileCreation; 
    3838 
    39 // By default, don't trace out details of the diff as we go along -- would fill up logs significantly. 
    40 // But it's useful for the test. 
    41 #ifndef NDEBUG 
    42         bool BackupStoreFile::TraceDetailsOfDiffProcess = false; 
    43 #endif 
    44  
    4539static void LoadIndex(IOStream &rBlockIndex, int64_t ThisID, BlocksAvailableEntry **ppIndex, int64_t &rNumBlocksOut, int Timeout, bool &rCanDiffFromThis); 
    46 static void FindMostUsedSizes(BlocksAvailableEntry *pIndex, int64_t NumBlocks, int32_t Sizes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]); 
    47 static void SearchForMatchingBlocks(IOStream &rFile,  
    48         std::map<int64_t, int64_t> &rFoundBlocks, BlocksAvailableEntry *pIndex,  
    49         int64_t NumBlocks, int32_t Sizes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES], 
    50         DiffTimer *pDiffTimer); 
     40static void SearchForMatchingBlocks(IOStream &rFile, std::map<int64_t, int64_t> &rFoundBlocks, BlocksAvailableEntry *pIndex, int64_t NumBlocks, DiffTimer *pDiffTimer); 
    5141static void SetupHashTable(BlocksAvailableEntry *pIndex, int64_t NumBlocks, int32_t BlockSize, BlocksAvailableEntry **pHashTable); 
    52 static bool SecondStageMatch(BlocksAvailableEntry *pFirstInHashList, RollingChecksum &fastSum, uint8_t *pBeginnings, uint8_t *pEndings, int Offset, int32_t BlockSize, int64_t FileBlockNumber, 
    53 BlocksAvailableEntry *pIndex, std::map<int64_t, int64_t> &rFoundBlocks); 
     42static bool SecondStageMatch(BlocksAvailableEntry *pFirstInHashList, RollingChecksum *pFastSum,  
     43                                                                uint8_t *pLow, int32_t LowSize, uint8_t *pHigh, int32_t HighSize, int32_t BlockSize, int64_t FileOffset, 
     44                                                                BlocksAvailableEntry *pIndex, std::map<int64_t, int64_t> &rFoundBlocks); 
    5445static void GenerateRecipe(BackupStoreFileEncodeStream::Recipe &rRecipe, BlocksAvailableEntry *pIndex, int64_t NumBlocks, std::map<int64_t, int64_t> &rFoundBlocks, int64_t SizeOfInputFile); 
    5546 
    5647// -------------------------------------------------------------------------- 
     
    166157         
    167158        try 
    168159        { 
    169                 // Find which sizes should be scanned 
    170                 int32_t sizesToScan[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
    171                 FindMostUsedSizes(pindex, blocksInIndex, sizesToScan); 
    172                  
    173160                // Flag for reporting to the user 
    174161                bool completelyDifferent; 
    175162                         
     
    184171                                // Get size of file 
    185172                                sizeOfInputFile = file.BytesLeftToRead(); 
    186173                                // Find all those lovely matching blocks 
    187                                 SearchForMatchingBlocks(file, foundBlocks, pindex,  
    188                                         blocksInIndex, sizesToScan, pDiffTimer); 
     174                                SearchForMatchingBlocks(file, foundBlocks, pindex, blocksInIndex, pDiffTimer); 
    189175                                 
    190176                                // Is it completely different? 
    191177                                completelyDifferent = (foundBlocks.size() == 0); 
     
    361347        } 
    362348} 
    363349 
    364  
    365350// -------------------------------------------------------------------------- 
    366351// 
    367352// Function 
    368 //              Name:    static FindMostUsedSizes(BlocksAvailableEntry *, int64_t, int32_t[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]) 
    369 //              Purpose: Finds the most commonly used block sizes in the index 
     353//              Name:    static SearchForMatchingBlocks(IOStream &, std::map<int64_t, int64_t> &, BlocksAvailableEntry *, int64_t, DiffTimer *) 
     354//              Purpose: Find the matching blocks within the file. 
    370355//              Created: 12/1/04 
    371356// 
    372357// -------------------------------------------------------------------------- 
    373 static void FindMostUsedSizes(BlocksAvailableEntry *pIndex, int64_t NumBlocks, int32_t Sizes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]) 
     358static void SearchForMatchingBlocks(IOStream &rFile, std::map<int64_t, int64_t> &rFoundBlocks, 
     359                                                                        BlocksAvailableEntry *pIndex, int64_t NumBlocks, DiffTimer *pDiffTimer) 
    374360{ 
    375         // Array for lengths 
    376         int64_t sizeCounts[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
     361        Timer maximumDiffingTime(0); 
    377362 
    378         // Set arrays to lots of zeros (= unused entries) 
    379         for(int l = 0; l < BACKUP_FILE_DIFF_MAX_BLOCK_SIZES; ++l) 
     363        if(pDiffTimer && pDiffTimer->IsManaged()) 
    380364        { 
    381                 Sizes[l] = 0; 
    382                 sizeCounts[l] = 0; 
     365                maximumDiffingTime = Timer(pDiffTimer->GetMaximumDiffingTime()); 
    383366        } 
    384367 
    385         // Array for collecting sizes 
    386         std::map<int32_t, int64_t> foundSizes; 
     368        // Flag to abort the run, if too many blocks are found or the diffing 
     369        // timeout expires 
     370        bool abortSearch = false; 
     371         
     372        // Buffers used during both phases of search 
     373        uint8_t *pbuffer0 = 0; 
     374        uint8_t *pbuffer1 = 0; 
     375         
     376        // Track offsets that already have block matches. We don't really care 
     377        // if its sorted, and this actually produces a performance issue, so  
     378        // see pfitBitmap for a workaround 
     379        std::map<int64_t, int32_t> goodnessOfFit; 
    387380         
    388         // Run through blocks and make a count of the entries 
    389         for(int64_t b = 0; b < NumBlocks; ++b) 
     381        // Collect sizes that aren't found in the file at their old offset 
     382        std::map<int32_t, int64_t> unmatchedSizes; 
     383         
     384        // Our arrays of block sizes during second search pass 
     385        int32_t scanSizes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
     386        int64_t scanSizesCount[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
     387        int32_t maxScanBlockSize = 0; 
     388        ::memset(scanSizes, 0, (sizeof(int32_t) * BACKUP_FILE_DIFF_MAX_BLOCK_SIZES)); 
     389        ::memset(scanSizesCount, 0, (sizeof(int64_t) * BACKUP_FILE_DIFF_MAX_BLOCK_SIZES)); 
     390 
     391        // We need to keep separate rolling checksums for each block size in second search 
     392        RollingChecksum *rollingSums[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
     393        ::memset(rollingSums, 0, (sizeof(RollingChecksum *) * BACKUP_FILE_DIFF_MAX_BLOCK_SIZES)); 
     394 
     395        // And a hash lookup table per block size in seacond search 
     396        BlocksAvailableEntry **hashTables[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
     397        ::memset(hashTables, 0, (sizeof(BlocksAvailableEntry **) * BACKUP_FILE_DIFF_MAX_BLOCK_SIZES)); 
     398         
     399        // We allow second search pass to short-circuit off rare blocks 
     400        bool scanThisSize[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
     401        ::memset(scanThisSize, 0, (sizeof(bool) * BACKUP_FILE_DIFF_MAX_BLOCK_SIZES)); 
     402         
     403        // During block read we have a bitmap of prefit locations to avoid std::map  
     404        // performance problems 
     405        uint8_t *pfitBitmap = 0; 
     406 
     407         
     408        // First search pass... 
     409        // 
     410        // For many files (especially large ones) most of the file is unchanged. 
     411        // The RollingChecksum process requires us to read every byte of the file looking for 
     412        // blocks that have moved. However, we can make that process more efficient by 
     413        // quickly rolling over areas that match a different block. We can also use 
     414        // this to eliminate entirely the rolling checksum for block sizes that only exist 
     415        // at one location in the file. 
     416        // 
     417        // Thus we start by looking for blocks that have not moved. Only if a block 
     418        // cannot be found at its previous location do we consider scanning for it by 
     419        // rolling checksum size. 
     420        //  
     421        // This strategy has some disadvantages for files with lots of repeating content 
     422        // that happens to align with our block size, but the reduction in diff time 
     423        // for more typical files is worth it. 
     424        // 
     425        // Note also that in this pass we consider _all_ block sizes (smaller than  
     426        // BACKUP_FILE_MAX_BLOCK_SIZE). Any block size, no matter how small or rare 
     427        // is cheap for us to find in this pass. 
     428        // 
     429        pbuffer0 = (uint8_t *)::malloc(BACKUP_FILE_MAX_BLOCK_SIZE); 
     430        try 
    390431        { 
    391                 // Only if the block size is bigger than the minimum size we'll scan for 
    392                 if(pIndex[b].mSize > BACKUP_FILE_DIFF_MIN_BLOCK_SIZE) 
     432                if(pbuffer0 == 0) 
     433                { 
     434                        throw std::bad_alloc(); 
     435                } 
     436                 
     437                // We have to track file offset since the read may fail 
     438                int64_t fileOffset = 0; 
     439                 
     440                // Walk the blocks 
     441                for(int64_t b = 0; b < NumBlocks; ++b) 
    393442                { 
    394                         // Find entry? 
    395                         std::map<int32_t, int64_t>::const_iterator f(foundSizes.find(pIndex[b].mSize)); 
    396                         if(f != foundSizes.end()) 
     443                        // Check diffing timeout 
     444                        if(maximumDiffingTime.HasExpired()) 
     445                        { 
     446                                ASSERT(pDiffTimer != NULL); 
     447                                BOX_INFO("MaximumDiffingTime reached - suspending file diff"); 
     448                                abortSearch = true; 
     449                                break; 
     450                        } 
     451                         
     452                        // Send keep alive 
     453                        if(pDiffTimer) pDiffTimer->DoKeepAlive(); 
     454                         
     455                        // Skip blocks too large for our buffer 
     456                        if(pIndex[b].mSize > BACKUP_FILE_MAX_BLOCK_SIZE) { 
     457                                fileOffset += pIndex[b].mSize; 
     458                                continue; 
     459                        } 
     460 
     461                        // Have to guard the seek operation, it could throw. We don't know size of the  
     462                        // current file, and checking is pointless anyway since there's a race. 
     463                        // In reality on Unix this is implemented with lseek which will mean we can 
     464                        // seek past the EOF, but I don't want to make assumptions about Win32. 
     465                        int32_t readSize = 0; 
     466                        try 
     467                        { 
     468                                rFile.Seek(fileOffset, IOStream::SeekType_Absolute); 
     469                                readSize = rFile.Read(pbuffer0, pIndex[b].mSize); 
     470                        } 
     471                        catch(BoxException &e) 
     472                        { 
     473                                if(e.GetType() != CommonException::ExceptionType || e.GetSubType() != CommonException::OSFileError) 
     474                                { 
     475                                        // Not what we expected, rethrow 
     476                                        throw; 
     477                                } 
     478                        } 
     479                         
     480                        // Check for a match 
     481                        bool blockMatched = false; 
     482                        if(readSize == pIndex[b].mSize) 
    397483                        { 
    398                                 // Increment existing entry 
    399                                 foundSizes[pIndex[b].mSize] = foundSizes[pIndex[b].mSize] + 1; 
     484                                // We don't have a rolling checksum to this point, so all we can do is MD5. If you 
     485                                // worry this is expensive just remember that prior versions of this code 
     486                                // re-read the file BACKUP_FILE_DIFF_MAX_BLOCK_SIZES times and calculated 
     487                                // rolling checksums every time. 
     488                                MD5Digest strong; 
     489                                strong.Add(pbuffer0, pIndex[b].mSize); 
     490                                strong.Finish(); 
     491                                 
     492                                // Do we have a match? 
     493                                if(strong.DigestMatches(pIndex[b].mStrongChecksum)) 
     494                                { 
     495#ifndef NDEBUG 
     496                                        BOX_TRACE("Found unchanged block of size " << pIndex[b].mSize << " at offset " << fileOffset); 
     497#endif                                                   
     498                                        rFoundBlocks[fileOffset] = b; 
     499                                        goodnessOfFit[fileOffset] = pIndex[b].mSize; 
     500                                        blockMatched = true; 
     501                                } 
    400502                        } 
    401                         else 
     503                         
     504                        // We're done with the file offset, so increment now 
     505                        fileOffset += pIndex[b].mSize; 
     506                         
     507                        // If the block didn't match then this is a size we'll have to scan for 
     508                        if(!blockMatched && (pIndex[b].mSize >= BACKUP_FILE_DIFF_MIN_BLOCK_SIZE)) 
    402509                        { 
    403                                 // New entry 
    404                                 foundSizes[pIndex[b].mSize] = 1; 
     510                                // Find entry? 
     511                                std::map<int32_t, int64_t>::const_iterator f(unmatchedSizes.find(pIndex[b].mSize)); 
     512                                if(f != unmatchedSizes.end()) 
     513                                { 
     514                                        // Increment existing entry 
     515                                        unmatchedSizes[pIndex[b].mSize] = unmatchedSizes[pIndex[b].mSize] + 1; 
     516                                } 
     517                                else 
     518                                { 
     519                                        // New entry 
     520                                        unmatchedSizes[pIndex[b].mSize] = 1; 
     521                                } 
    405522                        } 
    406523                } 
     524                 
     525                // Cleanup 
     526                ::free(pbuffer0); 
     527                pbuffer0 = 0; 
    407528        } 
    408          
    409         // Make the block sizes 
    410         for(std::map<int32_t, int64_t>::const_iterator i(foundSizes.begin()); i != foundSizes.end(); ++i) 
     529        catch(...) 
     530        { 
     531                // Cleanup and rethrow 
     532                if(pbuffer0 != 0) ::free(pbuffer0); 
     533                throw; 
     534        } 
     535                 
     536        // Are we already out of time? 
     537        if(abortSearch) 
    411538        { 
    412                 // Find the position of the size in the array 
     539#ifndef NDEBUG 
     540                goto dumpDiffList; 
     541#endif 
     542                return; 
     543        } 
     544         
     545         
     546        // Second search pass... 
     547        // 
     548        // In our second phase, having matched all unchanged blocks we now need 
     549        // to scan for moved blocks. This involves looping across all unmatched 
     550        // block sizes and using the rolling checksum to look for relocations. To keep 
     551        // this from being too expensive we cap at BACKUP_FILE_DIFF_MAX_BLOCK_SIZES 
     552        // for the number of sizes to scan. We also scan for the blocks in order of 
     553        // their relative probability, a block size that occurs frequently is scanned 
     554        // first. 
     555        // 
     556         
     557        // Loop all sizes inserting higher usages into the array 
     558        for(std::map<int32_t, int64_t>::const_iterator i(unmatchedSizes.begin()); i != unmatchedSizes.end(); ++i) 
     559        { 
     560                // TODO: Scanning for any block size isn't cheap, and realistically in many cases 
     561                // it would be less expensive to upload a few thousand bytes rather than do the scan. 
     562                // Here would be a good place to filter block sizes that aren't worth the effort 
     563                // once a suitable set of heuristics is found. 
     564                 
    413565                for(int t = 0; t < BACKUP_FILE_DIFF_MAX_BLOCK_SIZES; ++t) 
    414566                { 
    415                         // Instead of sorting on the raw count of blocks, 
    416                         // take the file area covered by this block size. 
    417                         if(i->second * i->first > sizeCounts[t] * Sizes[t]) 
     567                        // Instead of sorting on the raw count of blocks, take the file area covered by this 
     568                        // block size. This helps avoid favoring low numbers of large blocks over many 
     569                        // small blocks. 
     570                        if((i->second * i->first) > (scanSizesCount[t] * scanSizes[t])) 
    418571                        { 
    419572                                // Then this size belong before this entry -- shuffle them up 
    420573                                for(int s = (BACKUP_FILE_DIFF_MAX_BLOCK_SIZES - 1); s >= t; --s) 
    421574                                { 
    422                                         Sizes[s] = Sizes[s-1]; 
    423                                         sizeCounts[s] = sizeCounts[s-1]; 
     575                                        scanSizes[s] = scanSizes[s-1]; 
     576                                        scanSizesCount[s] = scanSizesCount[s-1]; 
    424577                                } 
    425578                                 
    426579                                // Insert this size 
    427                                 Sizes[t] = i->first; 
    428                                 sizeCounts[t] = i->second; 
     580                                scanSizes[t] = i->first; 
     581                                scanSizesCount[t] = i->second; 
    429582                                 
    430                                 // Shouldn't do any more searching 
     583                                // Update max size 
     584                                if(maxScanBlockSize < i->first) maxScanBlockSize = i->first; 
     585                                 
     586                                // Shouldn't do any more searching for this size 
    431587                                break; 
    432588                        } 
    433589                } 
    434590        } 
    435          
    436         // trace the size table in debug builds 
     591        // Dump the size table in debug builds 
    437592#ifndef NDEBUG 
    438         if(BackupStoreFile::TraceDetailsOfDiffProcess) 
     593        for(int t = 0; t < BACKUP_FILE_DIFF_MAX_BLOCK_SIZES; ++t) 
    439594        { 
    440                 for(int t = 0; t < BACKUP_FILE_DIFF_MAX_BLOCK_SIZES; ++t) 
    441                 { 
    442                         TRACE3("Diff block size %d: %d (count = %lld)\n", t, Sizes[t], sizeCounts[t]); 
    443                 } 
     595                if(scanSizes[t] != 0) BOX_TRACE("Scan block size " << t << ": " << scanSizes[t] << " count: " << scanSizesCount[t]); 
    444596        } 
    445597#endif 
    446 } 
    447  
    448  
    449  
    450 // -------------------------------------------------------------------------- 
    451 // 
    452 // Function 
    453 //              Name:    static SearchForMatchingBlocks(IOStream &, std::map<int64_t, int64_t> &, BlocksAvailableEntry *, int64_t, int32_t[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]) 
    454 //              Purpose: Find the matching blocks within the file. 
    455 //              Created: 12/1/04 
    456 // 
    457 // -------------------------------------------------------------------------- 
    458 static void SearchForMatchingBlocks(IOStream &rFile, std::map<int64_t, int64_t> &rFoundBlocks, 
    459         BlocksAvailableEntry *pIndex, int64_t NumBlocks,  
    460         int32_t Sizes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES], DiffTimer *pDiffTimer) 
    461 { 
    462         Timer maximumDiffingTime(0); 
    463598 
    464         if(pDiffTimer && pDiffTimer->IsManaged()) 
     599        // If we didn't find any sizes (could happen if all were found in the first matching 
     600        // phase) we're done. 
     601        if(maxScanBlockSize == 0) 
    465602        { 
    466                 maximumDiffingTime = Timer(pDiffTimer->GetMaximumDiffingTime()); 
     603#ifndef NDEBUG 
     604                BOX_TRACE("No scan block sizes, skip rolling checksum"); 
     605                goto dumpDiffList; 
     606#endif 
     607                return; 
    467608        } 
     609        ASSERT(maxScanBlockSize <= BACKUP_FILE_MAX_BLOCK_SIZE); 
    468610         
    469         std::map<int64_t, int32_t> goodnessOfFit; 
    470  
    471         // Allocate the hash lookup table 
    472         BlocksAvailableEntry **phashTable = (BlocksAvailableEntry **)::malloc(sizeof(BlocksAvailableEntry *) * (64*1024)); 
    473  
    474         // Choose a size for the buffer, just a little bit more than the maximum block size 
    475         int32_t bufSize = Sizes[0]; 
    476         for(int z = 1; z < BACKUP_FILE_DIFF_MAX_BLOCK_SIZES; ++z) 
    477         { 
    478                 if(Sizes[z] > bufSize) bufSize = Sizes[z]; 
    479         } 
    480         bufSize += 4; 
    481         ASSERT(bufSize > Sizes[0]); 
    482         ASSERT(bufSize > 0); 
    483         if(bufSize > (BACKUP_FILE_MAX_BLOCK_SIZE + 1024)) 
    484         { 
    485                 THROW_EXCEPTION(BackupStoreException, BadBackupStoreFile) 
    486         } 
     611        // Allocate two buffers we'll toggle between at the max scan block size 
     612        // There sizes are doubled to make final block at the end easier (we're cheating) 
     613        pbuffer0 = (uint8_t *)::malloc(maxScanBlockSize * 2); 
     614        pbuffer1 = (uint8_t *)::malloc(maxScanBlockSize * 2); 
     615         
     616        // Allocate a bitmap buffer to optimize goodnessOfFit access 
     617        pfitBitmap = (uint8_t *)::malloc(maxScanBlockSize * 2 / 8); 
    487618         
    488         // TODO: Because we read in the file a scanned block size at a time, 
    489         // it is likely to be inefficient. Probably will be much better to 
    490         // calculate checksums for all block sizes in a single pass. 
    491  
    492         // Allocate the buffers. 
    493         uint8_t *pbuffer0 = (uint8_t *)::malloc(bufSize); 
    494         uint8_t *pbuffer1 = (uint8_t *)::malloc(bufSize); 
    495619        try 
    496620        { 
    497621                // Check buffer allocation 
    498                 if(pbuffer0 == 0 || pbuffer1 == 0 || phashTable == 0) 
     622                if(pbuffer0 == 0 || pbuffer1 == 0 || pfitBitmap == 0) 
    499623                { 
    500624                        // If a buffer got allocated, it will be cleaned up in the catch block 
    501625                        throw std::bad_alloc(); 
    502626                } 
    503627                 
    504                 // Flag to abort the run, if too many blocks are found -- avoid using 
    505                 // huge amounts of processor time when files contain many similar blocks. 
    506                 bool abortSearch = false; 
    507                  
    508                 // Search for each block size in turn 
    509                 // NOTE: Do the smallest size first, so that the scheme for adding 
    510                 // entries in the found list works as expected and replaces smallers block 
    511                 // with larger blocks when it finds matches at the same offset in the file. 
    512                 for(int s = BACKUP_FILE_DIFF_MAX_BLOCK_SIZES - 1; s >= 0; --s) 
    513                 { 
    514                         ASSERT(Sizes[s] <= bufSize); 
    515                         BOX_TRACE("Diff pass " << s << ", for block size " << 
    516                                 Sizes[s]); 
    517                          
    518                         // Check we haven't finished 
    519                         if(Sizes[s] == 0) 
    520                         { 
    521                                 // empty entry, try next size 
    522                                 continue; 
    523                         } 
    524                          
    525                         // Set up the hash table entries 
    526                         SetupHashTable(pIndex, NumBlocks, Sizes[s], phashTable); 
     628                // Shift file position back to beginning 
     629                int64_t bufferFileOffset = 0; 
     630                rFile.Seek(0, IOStream::SeekType_Absolute); 
    527631                 
    528                         // Shift file position to beginning 
    529                         rFile.Seek(0, IOStream::SeekType_Absolute); 
    530                          
    531                         // Read first block 
    532                         if(rFile.Read(pbuffer0, Sizes[s]) != Sizes[s]) 
     632                // We're going to be flipping back and forth between two buffers, the low and high 
     633                uint8_t *lowBuffer = pbuffer0; 
     634                int32_t lowBufferBytes = 0; 
     635                uint8_t *highBuffer = pbuffer1; 
     636                int32_t highBufferBytes = 0; 
     637 
     638                // In some cases we need to carry over reads from prior buffers 
     639                int32_t carryOverBytes[BACKUP_FILE_DIFF_MAX_BLOCK_SIZES]; 
     640                ::memset(carryOverBytes, 0, (sizeof(int32_t) * BACKUP_FILE_DIFF_MAX_BLOCK_SIZES)); 
     641                 
     642                // Read the first buffer's worth of data 
     643                lowBufferBytes = rFile.Read(lowBuffer, maxScanBlockSize); 
     644                // Fill the second buffer if appropriate 
     645                if(lowBufferBytes == maxScanBlockSize) 
     646                { 
     647                        highBufferBytes = rFile.Read(highBuffer, maxScanBlockSize); 
     648                } 
     649                 
     650                // For every block size, initialize our scan tracking 
     651                for(int z = 0; z < BACKUP_FILE_DIFF_MAX_BLOCK_SIZES; ++z) 
     652                { 
     653                        ASSERT(scanSizes[z] <= maxScanBlockSize); 
     654 
     655                        // The sizes array may be mostly empty, in those cases we have no 
     656                        // state to maintain. 
     657                        if(scanSizes[z] != 0) 
    533658                        { 
    534                                 // Size of file too short to match -- do next size 
    535                                 continue; 
    536                         } 
    537                          
    538                         // Setup block pointers 
    539                         uint8_t *beginnings = pbuffer0; 
    540                         uint8_t *endings = pbuffer1; 
    541                         int offset = 0; 
    542                          
    543                         // Calculate the first checksum, ready for rolling 
    544                         RollingChecksum rolling(beginnings, Sizes[s]); 
    545                          
    546                         // Then roll, until the file is exhausted 
    547                         int64_t fileBlockNumber = 0; 
    548                         int64_t fileOffset = 0; 
    549                         int rollOverInitialBytes = 0; 
    550                         while(true) 
    551                         { 
    552                                 if(maximumDiffingTime.HasExpired()) 
    553                                 { 
    554                                         ASSERT(pDiffTimer != NULL); 
    555                                         BOX_INFO("MaximumDiffingTime reached - " 
    556                                                 "suspending file diff"); 
    557                                         abortSearch = true; 
    558                                         break; 
    559                                 } 
     659                                // Mark for scan 
     660                                scanThisSize[z] = true; 
    560661                                 
    561                                 if(pDiffTimer) 
     662                                // Set up the hash table for this size 
     663                                hashTables[z] = (BlocksAvailableEntry **)::malloc(sizeof(BlocksAvailableEntry *) * (64*1024)); 
     664                                if(hashTables[z] == 0) 
    562665                                { 
    563                                         pDiffTimer->DoKeepAlive(); 
     666                                        throw std::bad_alloc(); 
    564667                                } 
    565                                  
    566                                 // Load in another block of data, and record how big it is 
    567                                 int bytesInEndings = rFile.Read(endings, Sizes[s]); 
    568                                 int tmp; 
     668                                SetupHashTable(pIndex, NumBlocks, scanSizes[z], hashTables[z]); 
    569669 
    570                                 // Skip any bytes from a previous matched block 
    571                                 if(rollOverInitialBytes > 0 && offset < bytesInEndings) 
     670                                // Set up a rolling checksum, but only if there is enough data to start with 
     671                                // (file may now be shorter than some block sizes previously used) 
     672                                if(scanSizes[z] <= lowBufferBytes) 
    572673                                { 
    573                                         int spaceLeft = bytesInEndings - offset; 
    574                                         int thisRoll = (rollOverInitialBytes > spaceLeft) ? spaceLeft : rollOverInitialBytes; 
     674                                        rollingSums[z] = new RollingChecksum(lowBuffer, scanSizes[z]); 
     675                                } 
     676                        } 
     677                } 
     678                 
     679                // Read loop while we can get full maxScanBlockSize reads 
     680                while(highBufferBytes == maxScanBlockSize) 
     681                { 
     682                        // Oh happy day! We have maxScanBlockSize * 2 bytes available to us across 
     683                        // the two buffers, which means we can walk every block size across all offsets 
     684                        // in lowBuffer without any concern about overrunning the data available in highBuffer. 
    575685 
    576                                         rolling.RollForwardSeveral(beginnings+offset, endings+offset, Sizes[s], thisRoll); 
     686                        // Check diffing timeout