/*
Description: file names persistent cache
Author: Marco Costalba (C) 2005-2007
Copyright: See COPYING file that comes with this distribution
*/
#include
#include
#include "cache.h"
using namespace QGit;
bool Cache::save(const QString& gitDir, const RevFileMap& rf,
const StrVect& dirs, const StrVect& files) {
if (gitDir.isEmpty() || rf.isEmpty())
return false;
QString path(gitDir + C_DAT_FILE);
QString tmpPath(path + BAK_EXT);
QDir dir;
if (!dir.exists(gitDir)) {
dbs("Git directory not found, unable to save cache");
return false;
}
QFile f(tmpPath);
if (!f.open(QIODevice::WriteOnly | QIODevice::Unbuffered))
return false;
dbs("Saving cache. Please wait...");
// compress in memory before write to file
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
// Write a header with a "magic number" and a version
stream << (quint32)C_MAGIC;
stream << (qint32)C_VERSION;
stream << (qint32)dirs.count();
for (int i = 0; i < dirs.count(); ++i)
stream << dirs.at(i);
stream << (qint32)files.count();
for (int i = 0; i < files.count(); ++i)
stream << files.at(i);
// to achieve a better compression we save the sha's as
// one very long string instead of feeding the stream with
// each one. With this trick we gain a 15% size reduction
// in the final compressed file. The save/load speed is
// almost the same.
uint bufSize = rf.count() * 41 + 1000; // a little bit more space then required
QByteArray buf;
buf.reserve(bufSize);
QVector v;
v.reserve(rf.count());
QVector ba;
ShaString CUSTOM_SHA_RAW(toPersistentSha(CUSTOM_SHA, ba));
unsigned int newSize = 0;
FOREACH (RevFileMap, it, rf) {
const ShaString& sha = it.key();
if ( sha == ZERO_SHA_RAW
|| sha == CUSTOM_SHA_RAW
|| sha.latin1()[0] == 'A') // ALL_MERGE_FILES + rev sha
continue;
v.append(it.value());
buf.append(sha.latin1()).append('\0');
newSize += 41;
if (newSize > bufSize) {
dbs("ASSERT in Cache::save, out of allocated space");
return false;
}
}
buf.resize(newSize);
stream << (qint32)newSize;
stream << buf;
for (int i = 0; i < v.size(); ++i)
*(v.at(i)) >> stream;
dbs("Compressing data...");
f.write(qCompress(data, 1)); // no need to encode with compressed data
f.close();
// rename C_DAT_FILE + BAK_EXT -> C_DAT_FILE
if (dir.exists(path)) {
if (!dir.remove(path)) {
dbs("access denied to " + path);
dir.remove(tmpPath);
return false;
}
}
dir.rename(tmpPath, path);
dbs("Done.");
return true;
}
bool Cache::load(const QString& gitDir, RevFileMap& rfm,
StrVect& dirs, StrVect& files, QByteArray& revsFilesShaBuf) {
// check for cache file
QString path(gitDir + C_DAT_FILE);
QFile f(path);
if (!f.exists())
return true; // no cache file is not an error
if (!f.open(QIODevice::ReadOnly | QIODevice::Unbuffered))
return false;
QDataStream stream(qUncompress(f.readAll()));
quint32 magic;
qint32 version;
qint32 dirsNum, filesNum, bufSize;
stream >> magic;
stream >> version;
if (magic != C_MAGIC || version != C_VERSION) {
f.close();
return false;
}
// read the data
stream >> dirsNum;
dirs.resize(dirsNum);
for (int i = 0; i < dirsNum; ++i)
stream >> dirs[i];
stream >> filesNum;
files.resize(filesNum);
for (int i = 0; i < filesNum; ++i)
stream >> files[i];
stream >> bufSize;
revsFilesShaBuf.clear();
revsFilesShaBuf.reserve(bufSize);
stream >> revsFilesShaBuf;
const char* data = revsFilesShaBuf.constData();
while (!stream.atEnd()) {
RevFile* rf = new RevFile();
*rf << stream;
ShaString sha(data);
rfm.insert(sha, rf);
data += 40;
if (*data != '\0') {
dbp("ASSERT in Cache::load, corrupted SHA after %1", sha);
return false;
}
data++;
}
f.close();
return true;
}