summaryrefslogtreecommitdiff
path: root/lib/db_file.cpp
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:16:21 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:16:21 +0200
commit88a2d0007db222c339f0b6a17794a2014a241892 (patch)
tree75105ef49b3a52b7ee176a1ad480e7652e49825f /lib/db_file.cpp
parent4.2 (diff)
downloadFreeFileSync-88a2d0007db222c339f0b6a17794a2014a241892.tar.gz
FreeFileSync-88a2d0007db222c339f0b6a17794a2014a241892.tar.bz2
FreeFileSync-88a2d0007db222c339f0b6a17794a2014a241892.zip
4.3
Diffstat (limited to 'lib/db_file.cpp')
-rw-r--r--lib/db_file.cpp476
1 files changed, 204 insertions, 272 deletions
diff --git a/lib/db_file.cpp b/lib/db_file.cpp
index faee4c8a..60a721b1 100644
--- a/lib/db_file.cpp
+++ b/lib/db_file.cpp
@@ -15,7 +15,7 @@
#include <zen/file_io.h>
#include <zen/scope_guard.h>
#include <zen/guid.h>
-#include <boost/bind.hpp>
+#include <zen/utf8.h>
#ifdef FFS_WIN
#include <zen/win.h> //includes "windows.h"
@@ -29,15 +29,24 @@ namespace
{
//-------------------------------------------------------------------------------------------------------------------------------
const char FILE_FORMAT_DESCR[] = "FreeFileSync";
-const int FILE_FORMAT_VER = 7;
+const int FILE_FORMAT_VER = 8;
//-------------------------------------------------------------------------------------------------------------------------------
+typedef std::string UniqueId;
+typedef Zbase<char> MemoryStream; //ref-counted byte stream representing DirInformation
+typedef std::map<UniqueId, MemoryStream> StreamMapping; //list of streams ordered by session UUID
+
+
+//------------------------------------------------------------------------------------
+//| ensure 32/64 bit portability: used fixed size data types only e.g. std::uint32_t |
+//------------------------------------------------------------------------------------
+
template <SelectedSide side> inline
Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false)
{
- //Linux and Windows builds are binary incompatible: char/wchar_t case, sensitive/insensitive
- //32 and 64 bit db files ARE designed to be binary compatible!
+ //Linux and Windows builds are binary incompatible: different file id?, problem with case sensitivity?
+ //however 32 and 64 bit db files *are* designed to be binary compatible!
//Give db files different names.
//make sure they end with ".ffs_db". These files will not be included into comparison when located in base sync directories
#ifdef FFS_WIN
@@ -51,255 +60,209 @@ Zstring getDBFilename(const BaseDirMapping& baseMap, bool tempfile = false)
}
-
-class FileInputStreamDB : public FileInputStream
+StreamMapping loadStreams(const Zstring& filename) //throw FileError
{
-public:
- FileInputStreamDB(const Zstring& filename) : //throw FileError
- FileInputStream(filename)
+ if (!zen::fileExists(filename))
+ throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
+ _("One of the FreeFileSync database files is not yet existing:") + L" \n" +
+ L"\"" + filename + L"\"");
+ try
{
+ //read format description (uncompressed)
+ FileInputStream rawStream(filename); //throw FileError
+
//read FreeFileSync file identifier
char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {};
- Read(formatDescr, sizeof(formatDescr)); //throw FileError
+ rawStream.Read(formatDescr, sizeof(formatDescr)); //throw FileError
if (!std::equal(FILE_FORMAT_DESCR, FILE_FORMAT_DESCR + sizeof(FILE_FORMAT_DESCR), formatDescr))
throw FileError(_("Incompatible synchronization database format:") + L" \n" + L"\"" + filename + L"\"");
- }
-private:
-};
+ wxZlibInputStream decompressed(rawStream, wxZLIB_ZLIB);
+ CheckedReader cr(decompressed, filename);
-class FileOutputStreamDB : public FileOutputStream
-{
-public:
- FileOutputStreamDB(const Zstring& filename) : //throw FileError
- FileOutputStream(filename)
+ std::int32_t version = cr.readNumberC<std::int32_t>();
+ if (version != FILE_FORMAT_VER) //read file format version#
+ throw FileError(_("Incompatible synchronization database format:") + L" \n" + L"\"" + filename + L"\"");
+
+ //read stream lists
+ StreamMapping output;
+
+ std::uint32_t dbCount = cr.readNumberC<std::uint32_t>(); //number of databases: one for each sync-pair
+ while (dbCount-- != 0)
+ {
+ //DB id of partner databases
+ const std::string sessionID = cr.readStringC<std::string>();
+ const MemoryStream stream = cr.readStringC<MemoryStream>(); //read db-entry stream (containing DirInformation)
+
+ output.insert(std::make_pair(sessionID, stream));
+ }
+ return output;
+ }
+ catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file
{
- //write FreeFileSync file identifier
- Write(FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); //throw FileError
+ throw FileError(_("Error reading from synchronization database:") + L" (bad alloc)");
}
-
-private:
-};
}
-//#######################################################################################################################################
-class ReadDirInfo : public zen::ReadInputStream
+class StreamParser : private CheckedReader
{
public:
- ReadDirInfo(wxInputStream& stream, const Zstring& errorObjName, DirInformation& dirInfo) : ReadInputStream(stream, errorObjName)
+ static DirInfoPtr execute(const MemoryStream& stream, const Zstring& fileName) //throw FileError -> return value always bound!
{
- //|-------------------------------------------------------------------------------------
- //| ensure 32/64 bit portability: use fixed size data types only e.g. boost::uint32_t |
- //|-------------------------------------------------------------------------------------
-
- //read filter settings -> currently not required, but persisting it doesn't hurt
- dirInfo.filter = HardFilter::loadFilter(getStream());
- check();
-
- //start recursion
- execute(dirInfo.baseDirContainer);
+ try
+ {
+ //read streams into DirInfo
+ auto dirInfo = std::make_shared<DirInformation>();
+ wxMemoryInputStream buffer(&*stream.begin(), stream.size()); //convert char-array to inputstream: no copying, ownership not transferred
+ StreamParser(buffer, fileName, *dirInfo); //throw FileError
+ return dirInfo;
+ }
+ catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file
+ {
+ throw FileError(_("Error reading from synchronization database:") + L" (bad alloc)");
+ }
}
private:
- void execute(DirContainer& dirCont) const
+ StreamParser(wxInputStream& stream, const Zstring& errorObjName, DirInformation& dirInfo) : CheckedReader(stream, errorObjName)
{
- while (readNumberC<bool>())
- readSubFile(dirCont);
-
- while (readNumberC<bool>())
- readSubLink(dirCont);
-
- while (readNumberC<bool>())
- readSubDirectory(dirCont);
+ recurse(dirInfo.baseDirContainer);
}
- void readSubFile(DirContainer& dirCont) const
+ Zstring readStringUtf8() const
{
- //attention: order of function argument evaluation is undefined! So do it one after the other...
- const Zstring shortName = readStringC<Zstring>(); //file name
-
- const std::int64_t modTime = readNumberC<std::int64_t>();
- const std::uint64_t fileSize = readNumberC<std::uint64_t>();
-
- //const util::FileID fileIdentifier(stream_);
- //check();
-
- dirCont.addSubFile(shortName,
- FileDescriptor(modTime, fileSize));
+ return utf8CvrtTo<Zstring>(readStringC<Zbase<char>>());
}
-
- void readSubLink(DirContainer& dirCont) const
+ FileId readFileId() const
{
- //attention: order of function argument evaluation is undefined! So do it one after the other...
- const Zstring shortName = readStringC<Zstring>(); //file name
- const std::int64_t modTime = readNumberC<std::int64_t>();
- const Zstring targetPath = readStringC<Zstring>(); //file name
- const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readNumberC<std::int32_t>());
-
- dirCont.addSubLink(shortName,
- LinkDescriptor(modTime, targetPath, linkType));
- }
+ assert_static(sizeof(FileId().first ) <= sizeof(std::uint64_t));
+ assert_static(sizeof(FileId().second) <= sizeof(std::uint64_t));
-
- void readSubDirectory(DirContainer& dirCont) const
- {
- const Zstring shortName = readStringC<Zstring>(); //directory name
- DirContainer& subDir = dirCont.addSubDir(shortName);
- execute(subDir); //recurse
+ const auto devId = static_cast<decltype(FileId().first )>(readNumberC<std::uint64_t>()); //
+ const auto fId = static_cast<decltype(FileId().second)>(readNumberC<std::uint64_t>()); //silence "loss of precision" compiler warnings
+ return std::make_pair(devId, fId);
}
-};
-namespace
-{
-typedef std::string UniqueId;
-typedef std::shared_ptr<std::vector<char> > MemoryStreamPtr; //byte stream representing DirInformation
-typedef std::map<UniqueId, MemoryStreamPtr> StreamMapping; //list of streams ordered by session UUID
-}
-
-class ReadFileStream : public zen::ReadInputStream
-{
-public:
- ReadFileStream(wxInputStream& stream, const Zstring& filename, StreamMapping& streamList) : ReadInputStream(stream, filename)
+ void recurse(DirContainer& dirCont) const
{
- //|-------------------------------------------------------------------------------------
- //| ensure 32/64 bit portability: used fixed size data types only e.g. boost::uint32_t |
- //|-------------------------------------------------------------------------------------
-
- std::int32_t version = readNumberC<std::int32_t>();
+ while (readNumberC<bool>()) //files
+ {
+ //attention: order of function argument evaluation is undefined! So do it one after the other...
+ const Zstring shortName = readStringUtf8(); //file name
- if (version != FILE_FORMAT_VER) //read file format version
- throw FileError(_("Incompatible synchronization database format:") + L" \n" + L"\"" + filename + L"\"");
+ const std::int64_t modTime = readNumberC<std::int64_t>();
+ const std::uint64_t fileSize = readNumberC<std::uint64_t>();
+ const FileId fileID = readFileId();
- streamList.clear();
+ dirCont.addSubFile(shortName,
+ FileDescriptor(modTime, fileSize, fileID));
+ }
- boost::uint32_t dbCount = readNumberC<boost::uint32_t>(); //number of databases: one for each sync-pair
- while (dbCount-- != 0)
+ while (readNumberC<bool>()) //symlinks
{
- //DB id of partner databases
- const CharArray tmp2 = readArrayC();
- const std::string sessionID(tmp2->begin(), tmp2->end());
-
- CharArray buffer = readArrayC(); //read db-entry stream (containing DirInformation)
+ //attention: order of function argument evaluation is undefined! So do it one after the other...
+ const Zstring shortName = readStringUtf8(); //file name
+ const std::int64_t modTime = readNumberC<std::int64_t>();
+ const Zstring targetPath = readStringUtf8(); //file name
+ const LinkDescriptor::LinkType linkType = static_cast<LinkDescriptor::LinkType>(readNumberC<std::int32_t>());
+
+ dirCont.addSubLink(shortName,
+ LinkDescriptor(modTime, targetPath, linkType));
+ }
- streamList.insert(std::make_pair(sessionID, buffer));
+ while (readNumberC<bool>()) //directories
+ {
+ const Zstring shortName = readStringUtf8(); //directory name
+ DirContainer& subDir = dirCont.addSubDir(shortName);
+ recurse(subDir);
}
}
};
-namespace
-{
-StreamMapping loadStreams(const Zstring& filename) //throw FileError
-{
- if (!zen::fileExists(filename))
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
- _("One of the FreeFileSync database files is not yet existing:") + L" \n" +
- L"\"" + filename + L"\"");
-
- try
- {
- //read format description (uncompressed)
- FileInputStreamDB uncompressed(filename); //throw FileError
-
- wxZlibInputStream input(uncompressed, wxZLIB_ZLIB);
- StreamMapping streamList;
- ReadFileStream(input, filename, streamList);
- return streamList;
- }
- catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file
+//save/load DirContainer
+void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw FileError
+{
{
- throw FileError(_("Error reading from synchronization database:") + L" (bad_alloc)");
- }
-}
+ FileOutputStream rawStream(filename); //throw FileError
+ //write FreeFileSync file identifier
+ rawStream.Write(FILE_FORMAT_DESCR, sizeof(FILE_FORMAT_DESCR)); //throw FileError
-DirInfoPtr parseStream(const std::vector<char>& stream, const Zstring& fileName) //throw FileError -> return value always bound!
-{
- try
- {
- //read streams into DirInfo
- auto dirInfo = std::make_shared<DirInformation>();
- wxMemoryInputStream buffer(&stream[0], stream.size()); //convert char-array to inputstream: no copying, ownership not transferred
- ReadDirInfo(buffer, fileName, *dirInfo); //throw FileError
- return dirInfo;
- }
- catch (const std::bad_alloc&) //this is most likely caused by a corrupted database file
- {
- throw FileError(_("Error reading from synchronization database:") + L" (bad_alloc)");
- }
-}
-}
+ wxZlibOutputStream compressed(rawStream, 4, wxZLIB_ZLIB);
+ /* 4 - best compromise between speed and compression: (scanning 200.000 objects)
+ 0 (uncompressed) 8,95 MB - 422 ms
+ 2 2,07 MB - 470 ms
+ 4 1,87 MB - 500 ms
+ 6 1,77 MB - 613 ms
+ 9 (maximal compression) 1,74 MB - 3330 ms */
+ CheckedWriter cw(compressed, filename);
-std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw FileError
-{
- const Zstring fileNameLeft = getDBFilename<LEFT_SIDE>(baseMapping);
- const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseMapping);
+ //save file format version
+ cw.writeNumberC<std::int32_t>(FILE_FORMAT_VER);
- //read file data: list of session ID + DirInfo-stream
- const StreamMapping streamListLeft = ::loadStreams(fileNameLeft); //throw FileError
- const StreamMapping streamListRight = ::loadStreams(fileNameRight); //throw FileError
+ //save stream list
+ cw.writeNumberC<std::uint32_t>(static_cast<std::uint32_t>(streamList.size())); //number of database records: one for each sync-pair
- //find associated session: there can be at most one session within intersection of left and right ids
- StreamMapping::const_iterator streamLeft = streamListLeft .end();
- StreamMapping::const_iterator streamRight = streamListRight.end();
- for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft)
- {
- auto iterRight = streamListRight.find(iterLeft->first);
- if (iterRight != streamListRight.end())
+ for (auto iter = streamList.begin(); iter != streamList.end(); ++iter)
{
- streamLeft = iterLeft;
- streamRight = iterRight;
- break;
+ cw.writeStringC<std::string >(iter->first ); //sync session id
+ cw.writeStringC<MemoryStream>(iter->second); //DirInformation stream
}
}
-
- if (streamLeft == streamListLeft .end() ||
- streamRight == streamListRight.end() ||
- !streamLeft ->second.get() ||
- !streamRight->second.get())
- throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
- _("Database files do not share a common synchronization session:") + L" \n" +
- L"\"" + fileNameLeft + L"\"\n" +
- L"\"" + fileNameRight + L"\"");
- //read streams into DirInfo
- DirInfoPtr dirInfoLeft = parseStream(*streamLeft ->second, fileNameLeft); //throw FileError
- DirInfoPtr dirInfoRight = parseStream(*streamRight->second, fileNameRight); //throw FileError
-
- return std::make_pair(dirInfoLeft, dirInfoRight);
+ //(try to) hide database file
+#ifdef FFS_WIN
+ ::SetFileAttributes(zen::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN);
+#endif
}
-//-------------------------------------------------------------------------------------------------------------------------
template <SelectedSide side>
-class SaveDirInfo : public WriteOutputStream
+class StreamGenerator : private CheckedWriter
{
public:
- SaveDirInfo(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName, wxOutputStream& stream) : WriteOutputStream(errorObjName, stream)
+ static MemoryStream execute(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName)
{
- //save filter settings
- baseMapping.getFilter()->saveFilter(getStream());
- check();
+ wxMemoryOutputStream buffer;
+ StreamGenerator(baseMapping, oldDirInfo, errorObjName, buffer);
- //start recursion
- execute(baseMapping, oldDirInfo);
+ MemoryStream output;
+ output.resize(buffer.GetSize());
+ buffer.CopyTo(&*output.begin(), buffer.GetSize());
+ return output;
}
private:
- void execute(const HierarchyObject& hierObj, const DirContainer* oldDirInfo)
+ StreamGenerator(const BaseDirMapping& baseMapping, const DirContainer* oldDirInfo, const Zstring& errorObjName, wxOutputStream& stream) : CheckedWriter(stream, errorObjName)
+ {
+ recurse(baseMapping, oldDirInfo);
+ }
+
+ void recurse(const HierarchyObject& hierObj, const DirContainer* oldDirInfo)
{
- std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), boost::bind(&SaveDirInfo::processFile, this, _1, oldDirInfo));
+ // for (const auto& fileMap : hierObj.refSubFiles()) { processFile(fileMap, oldDirInfo); }); !
+
+ std::for_each(hierObj.refSubFiles().begin(), hierObj.refSubFiles().end(), [&](const FileMapping& fileMap) { this->processFile(fileMap, oldDirInfo); });
writeNumberC<bool>(false); //mark last entry
- std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), boost::bind(&SaveDirInfo::processLink, this, _1, oldDirInfo));
+ std::for_each(hierObj.refSubLinks().begin(), hierObj.refSubLinks().end(), [&](const SymLinkMapping& linkObj) { this->processLink(linkObj, oldDirInfo); });
writeNumberC<bool>(false); //mark last entry
- std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), boost::bind(&SaveDirInfo::processDir, this, _1, oldDirInfo));
+ std::for_each(hierObj.refSubDirs ().begin(), hierObj.refSubDirs ().end(), [&](const DirMapping& dirMap) { this->processDir(dirMap, oldDirInfo); });
writeNumberC<bool>(false); //mark last entry
}
+ void writeStringUtf8(const Zstring& str) { writeStringC(utf8CvrtTo<Zbase<char>>(str)); }
+
+ void writeFileId(const FileId& id)
+ {
+ writeNumberC<std::uint64_t>(id.first ); //device id
+ writeNumberC<std::uint64_t>(id.second); //file id
+ }
+
void processFile(const FileMapping& fileMap, const DirContainer* oldParentDir)
{
if (fileMap.getCategory() == FILE_EQUAL) //data in sync: write current state
@@ -307,9 +270,10 @@ private:
if (!fileMap.isEmpty<side>())
{
writeNumberC<bool>(true); //mark beginning of entry
- writeStringC(fileMap.getShortName<side>()); //save respecting case! (Windows)
- writeNumberC<std:: int64_t>(to<std:: int64_t>(fileMap.getLastWriteTime<side>())); //last modification time
- writeNumberC<std::uint64_t>(to<std::uint64_t>(fileMap.getFileSize<side>())); //filesize
+ writeStringUtf8(fileMap.getShortName<side>()); //save respecting case! (Windows)
+ writeNumberC<std:: int64_t>(to<std:: int64_t>(fileMap.getLastWriteTime<side>()));
+ writeNumberC<std::uint64_t>(to<std::uint64_t>(fileMap.getFileSize<side>()));
+ writeFileId(fileMap.getFileId<side>());
}
}
else //not in sync: reuse last synchronous state
@@ -320,9 +284,10 @@ private:
if (iter != oldParentDir->files.end())
{
writeNumberC<bool>(true); //mark beginning of entry
- writeStringC(iter->first); //save respecting case! (Windows)
- writeNumberC<std:: int64_t>(to<std:: int64_t>(iter->second.lastWriteTimeRaw)); //last modification time
- writeNumberC<std::uint64_t>(to<std::uint64_t>(iter->second.fileSize)); //filesize
+ writeStringUtf8(iter->first); //save respecting case! (Windows)
+ writeNumberC<std:: int64_t>(to<std:: int64_t>(iter->second.lastWriteTimeRaw));
+ writeNumberC<std::uint64_t>(to<std::uint64_t>(iter->second.fileSize));
+ writeFileId(iter->second.id);
}
}
}
@@ -335,9 +300,9 @@ private:
if (!linkObj.isEmpty<side>())
{
writeNumberC<bool>(true); //mark beginning of entry
- writeStringC(linkObj.getShortName<side>()); //save respecting case! (Windows)
- writeNumberC<std::int64_t>(to<std::int64_t>(linkObj.getLastWriteTime<side>())); //last modification time
- writeStringC(linkObj.getTargetPath<side>());
+ writeStringUtf8(linkObj.getShortName<side>()); //save respecting case! (Windows)
+ writeNumberC<std::int64_t>(to<std::int64_t>(linkObj.getLastWriteTime<side>()));
+ writeStringUtf8(linkObj.getTargetPath<side>());
writeNumberC<std::int32_t>(linkObj.getLinkType<side>());
}
}
@@ -349,9 +314,9 @@ private:
if (iter != oldParentDir->links.end())
{
writeNumberC<bool>(true); //mark beginning of entry
- writeStringC(iter->first); //save respecting case! (Windows)
- writeNumberC<std::int64_t>(to<std::int64_t>(iter->second.lastWriteTimeRaw)); //last modification time
- writeStringC(iter->second.targetPath);
+ writeStringUtf8(iter->first); //save respecting case! (Windows)
+ writeNumberC<std::int64_t>(to<std::int64_t>(iter->second.lastWriteTimeRaw));
+ writeStringUtf8(iter->second.targetPath);
writeNumberC<std::int32_t>(iter->second.type);
}
}
@@ -379,8 +344,8 @@ private:
if (!dirMap.isEmpty<side>())
{
writeNumberC<bool>(true); //mark beginning of entry
- writeStringC(dirMap.getShortName<side>()); //save respecting case! (Windows)
- execute(dirMap, oldDir); //recurse
+ writeStringUtf8(dirMap.getShortName<side>()); //save respecting case! (Windows)
+ recurse(dirMap, oldDir);
}
}
else //not in sync: reuse last synchronous state
@@ -388,8 +353,8 @@ private:
if (oldDir)
{
writeNumberC<bool>(true); //mark beginning of entry
- writeStringC(*oldDirName); //save respecting case! (Windows)
- execute(dirMap, oldDir); //recurse
+ writeStringUtf8(*oldDirName); //save respecting case! (Windows)
+ recurse(dirMap, oldDir);
return;
}
//no data is also a "synchronous state"!
@@ -408,69 +373,53 @@ private:
break;
case DIR_DIFFERENT_METADATA:
writeNumberC<bool>(true);
- writeStringC(dirMap.getShortName<side>());
+ writeStringUtf8(dirMap.getShortName<side>());
//ATTENTION: strictly this is a violation of the principle of reporting last synchronous state!
//however in this case this will result in "last sync unsuccessful" for this directory within <automatic> algorithm, which is fine
- execute(dirMap, oldDir); //recurse and save sub-items which are in sync
+ recurse(dirMap, oldDir); //recurse and save sub-items which are in sync
break;
}
}
}
};
+}
+//#######################################################################################################################################
-class WriteFileStream : public WriteOutputStream
+std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw FileError
{
-public:
- WriteFileStream(const StreamMapping& streamList, const Zstring& filename, wxOutputStream& stream) : WriteOutputStream(filename, stream)
- {
- //save file format version
- writeNumberC<std::int32_t>(FILE_FORMAT_VER);
+ const Zstring fileNameLeft = getDBFilename<LEFT_SIDE >(baseMapping);
+ const Zstring fileNameRight = getDBFilename<RIGHT_SIDE>(baseMapping);
- writeNumberC<boost::uint32_t>(static_cast<boost::uint32_t>(streamList.size())); //number of database records: one for each sync-pair
+ //read file data: list of session ID + DirInfo-stream
+ const StreamMapping streamListLeft = ::loadStreams(fileNameLeft); //throw FileError
+ const StreamMapping streamListRight = ::loadStreams(fileNameRight); //throw FileError
- for (StreamMapping::const_iterator i = streamList.begin(); i != streamList.end(); ++i)
+ //find associated session: there can be at most one session within intersection of left and right ids
+ StreamMapping::const_iterator streamLeft = streamListLeft .end();
+ StreamMapping::const_iterator streamRight = streamListRight.end();
+ for (auto iterLeft = streamListLeft.begin(); iterLeft != streamListLeft.end(); ++iterLeft)
+ {
+ auto iterRight = streamListRight.find(iterLeft->first);
+ if (iterRight != streamListRight.end())
{
- //sync session id
- writeArrayC(std::vector<char>(i->first.begin(), i->first.end()));
-
- //write DirInformation stream
- writeArrayC(*(i->second));
+ streamLeft = iterLeft;
+ streamRight = iterRight;
+ break;
}
}
-};
+ if (streamLeft == streamListLeft .end() ||
+ streamRight == streamListRight.end())
+ throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + L" \n\n" +
+ _("Database files do not share a common synchronization session:") + L" \n" +
+ L"\"" + fileNameLeft + L"\"\n" +
+ L"\"" + fileNameRight + L"\"");
+ //read streams into DirInfo
+ DirInfoPtr dirInfoLeft = StreamParser::execute(streamLeft ->second, fileNameLeft); //throw FileError
+ DirInfoPtr dirInfoRight = StreamParser::execute(streamRight->second, fileNameRight); //throw FileError
-//save/load DirContainer
-void saveFile(const StreamMapping& streamList, const Zstring& filename) //throw FileError
-{
- {
- //write format description (uncompressed)
- FileOutputStreamDB uncompressed(filename); //throw FileError
-
- wxZlibOutputStream output(uncompressed, 4, wxZLIB_ZLIB);
- /* 4 - best compromise between speed and compression: (scanning 200.000 objects)
- 0 (uncompressed) 8,95 MB - 422 ms
- 2 2,07 MB - 470 ms
- 4 1,87 MB - 500 ms
- 6 1,77 MB - 613 ms
- 9 (maximal compression) 1,74 MB - 3330 ms */
-
- WriteFileStream(streamList, filename, output);
- }
- //(try to) hide database file
-#ifdef FFS_WIN
- ::SetFileAttributes(zen::applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_HIDDEN);
-#endif
-}
-
-
-bool equalEntry(const MemoryStreamPtr& lhs, const MemoryStreamPtr& rhs)
-{
- if (!lhs.get() || !rhs.get())
- return lhs.get() == rhs.get();
-
- return *lhs == *rhs;
+ return std::make_pair(dirInfoLeft, dirInfoRight);
}
@@ -522,12 +471,10 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw FileError
try
{
if (streamLeft != streamListLeft .end() &&
- streamRight != streamListRight.end() &&
- streamLeft ->second.get() &&
- streamRight->second.get())
+ streamRight != streamListRight.end())
{
- oldDirInfoLeft = parseStream(*streamLeft ->second, dbNameLeft); //throw FileError
- oldDirInfoRight = parseStream(*streamRight->second, dbNameRight); //throw FileError
+ oldDirInfoLeft = StreamParser::execute(streamLeft ->second, dbNameLeft ); //throw FileError
+ oldDirInfoRight = StreamParser::execute(streamRight->second, dbNameRight); //throw FileError
}
}
catch (FileError&)
@@ -538,28 +485,13 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw FileError
}
//create new database entries
- MemoryStreamPtr newStreamLeft = std::make_shared<std::vector<char>>();
- {
- wxMemoryOutputStream buffer;
- const DirContainer* oldDir = oldDirInfoLeft.get() ? &oldDirInfoLeft->baseDirContainer : NULL;
- SaveDirInfo<LEFT_SIDE>(baseMapping, oldDir, dbNameLeft, buffer);
- newStreamLeft->resize(buffer.GetSize()); //convert output stream to char-array
- buffer.CopyTo(&(*newStreamLeft)[0], buffer.GetSize()); //
- }
-
- MemoryStreamPtr newStreamRight = std::make_shared<std::vector<char>>();
- {
- wxMemoryOutputStream buffer;
- const DirContainer* oldDir = oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL;
- SaveDirInfo<RIGHT_SIDE>(baseMapping, oldDir, dbNameRight, buffer);
- newStreamRight->resize(buffer.GetSize()); //convert output stream to char-array
- buffer.CopyTo(&(*newStreamRight)[0], buffer.GetSize()); //
- }
+ MemoryStream newStreamLeft = StreamGenerator<LEFT_SIDE >::execute(baseMapping, oldDirInfoLeft .get() ? &oldDirInfoLeft ->baseDirContainer : NULL, dbNameLeft);
+ MemoryStream newStreamRight = StreamGenerator<RIGHT_SIDE>::execute(baseMapping, oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL, dbNameRight);
//check if there is some work to do at all
{
- const bool updateRequiredLeft = streamLeft == streamListLeft .end() || !equalEntry(newStreamLeft, streamLeft ->second);
- const bool updateRequiredRight = streamRight == streamListRight.end() || !equalEntry(newStreamRight, streamRight->second);
+ const bool updateRequiredLeft = streamLeft == streamListLeft .end() || newStreamLeft != streamLeft ->second;
+ const bool updateRequiredRight = streamRight == streamListRight.end() || newStreamRight != streamRight->second;
//some users monitor the *.ffs_db file with RTS => don't touch the file if it isnt't strictly needed
if (!updateRequiredLeft && !updateRequiredRight)
return;
@@ -579,10 +511,10 @@ void zen::saveToDisk(const BaseDirMapping& baseMapping) //throw FileError
streamListRight.insert(std::make_pair(sessionID, newStreamRight));
//write (temp-) files...
- zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&]() {zen::removeFile(dbNameLeftTmp); });
+ zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); });
saveFile(streamListLeft, dbNameLeftTmp); //throw FileError
- zen::ScopeGuard guardTempFileRight = zen::makeGuard([&]() {zen::removeFile(dbNameRightTmp); });
+ zen::ScopeGuard guardTempFileRight = zen::makeGuard([&] {zen::removeFile(dbNameRightTmp); });
saveFile(streamListRight, dbNameRightTmp); //throw FileError
//operation finished: rename temp files -> this should work transactionally:
bgstack15