zbackup/chunk_index.cc

154 lines
3.8 KiB
C++
Raw Normal View History

2013-07-18 21:33:25 +04:00
// Copyright (c) 2012-2013 Konstantin Isakov <ikm@zbackup.org>
// Part of ZBackup. Licensed under GNU GPLv2 or later
#include <stdio.h>
#include <string.h>
#include <new>
#include <utility>
#include "chunk_index.hh"
#include "debug.hh"
#include "dir.hh"
#include "index_file.hh"
#include "zbackup.pb.h"
ChunkIndex::Chain::Chain( ChunkId const & id, Bundle::Id const * bundleId ):
next( 0 ), bundleId( bundleId )
{
memcpy( cryptoHash, id.cryptoHash, sizeof( cryptoHash ) );
}
bool ChunkIndex::Chain::equalsTo( ChunkId const & id )
{
return memcmp( cryptoHash, id.cryptoHash, sizeof ( cryptoHash ) ) == 0;
}
void ChunkIndex::loadIndex()
{
Dir::Listing lst( indexPath );
Dir::Entry entry;
verbosePrintf( "Loading index...\n" );
while( lst.getNext( entry ) )
{
verbosePrintf( "Loading index file %s...\n", entry.getFileName().c_str() );
IndexFile::Reader reader( key,
Dir::addPath( indexPath, entry.getFileName() ) );
BundleInfo info;
Bundle::Id bundleId;
while( reader.readNextRecord( info, bundleId ) )
{
Bundle::Id * savedId = storage.allocateObjects< Bundle::Id >( 1 );
memcpy( savedId, &bundleId, sizeof( bundleId ) );
lastBundleId = savedId;
ChunkId id;
for ( int x = info.chunk_record_size(); x--; )
{
BundleInfo_ChunkRecord const & record = info.chunk_record( x );
if ( record.id().size() != ChunkId::BlobSize )
throw exIncorrectChunkIdSize();
id.setFromBlob( record.id().data() );
registerNewChunkId( id, savedId );
}
}
}
verbosePrintf( "Index loaded.\n" );
}
ChunkIndex::ChunkIndex( EncryptionKey const & key, TmpMgr & tmpMgr,
string const & indexPath ):
key( key ), tmpMgr( tmpMgr ), indexPath( indexPath ), storage( 65536, 1 ),
lastBundleId( NULL )
{
loadIndex();
}
Bundle::Id const * ChunkIndex::findChunk( ChunkId::RollingHashPart rollingHash,
ChunkInfoInterface & chunkInfo )
{
HashTable::iterator i = hashTable.find( rollingHash );
ChunkId const * id = 0;
if ( i != hashTable.end() )
{
if ( !id )
id = &chunkInfo.getChunkId();
// Check the chains
for ( Chain * chain = i->second; chain; chain = chain->next )
if ( chain->equalsTo( *id ) )
return chain->bundleId;
}
return NULL;
}
namespace {
struct ChunkInfoImmediate: public ChunkIndex::ChunkInfoInterface
{
ChunkId const & id;
ChunkInfoImmediate( ChunkId const & id ): id( id ) {}
virtual ChunkId const & getChunkId()
{ return id; }
};
}
Bundle::Id const * ChunkIndex::findChunk( ChunkId const & chunkId )
{
ChunkInfoImmediate chunkInfo( chunkId );
return findChunk( chunkId.rollingHash, chunkInfo );
}
ChunkIndex::Chain * ChunkIndex::registerNewChunkId( ChunkId const & id,
Bundle::Id const * bundleId )
{
HashTable::iterator i =
hashTable.insert( std::make_pair( id.rollingHash, ( Chain *) 0 ) ).first;
Chain ** chain = &i->second;
2013-07-18 21:33:25 +04:00
// Check the chains
for ( ; *chain; chain = &( ( *chain )->next ) )
if ( ( *chain )->equalsTo( id ) )
2013-07-18 21:33:25 +04:00
{
return NULL; // The entry existed already
}
// Create a new chain
*chain = new ( storage.allocateObjects< Chain >( 1 ) ) Chain( id, bundleId );
2013-07-18 21:33:25 +04:00
return *chain;
2013-07-18 21:33:25 +04:00
}
bool ChunkIndex::addChunk( ChunkId const & id, Bundle::Id const & bundleId )
{
if ( Chain * chain = registerNewChunkId( id, NULL ) )
{
// Allocate or re-use bundle id
if ( !lastBundleId || *lastBundleId != bundleId )
{
Bundle::Id * allocatedId = storage.allocateObjects< Bundle::Id >( 1 );
memcpy( allocatedId, &bundleId, Bundle::IdSize );
lastBundleId = allocatedId;
}
chain->bundleId = lastBundleId;
return true;
}
else
return false;
}