From 88a2d0007db222c339f0b6a17794a2014a241892 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 17:16:21 +0200 Subject: 4.3 --- lib/db_file.cpp | 476 ++++++++++++++++++++++++-------------------------------- 1 file changed, 204 insertions(+), 272 deletions(-) (limited to 'lib/db_file.cpp') 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 #include #include -#include +#include #ifdef FFS_WIN #include //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 MemoryStream; //ref-counted byte stream representing DirInformation +typedef std::map 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 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(); + 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(); //number of databases: one for each sync-pair + while (dbCount-- != 0) + { + //DB id of partner databases + const std::string sessionID = cr.readStringC(); + const MemoryStream stream = cr.readStringC(); //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(); + 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()) - readSubFile(dirCont); - - while (readNumberC()) - readSubLink(dirCont); - - while (readNumberC()) - 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(); //file name - - const std::int64_t modTime = readNumberC(); - const std::uint64_t fileSize = readNumberC(); - - //const util::FileID fileIdentifier(stream_); - //check(); - - dirCont.addSubFile(shortName, - FileDescriptor(modTime, fileSize)); + return utf8CvrtTo(readStringC>()); } - - 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(); //file name - const std::int64_t modTime = readNumberC(); - const Zstring targetPath = readStringC(); //file name - const LinkDescriptor::LinkType linkType = static_cast(readNumberC()); - - 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(); //directory name - DirContainer& subDir = dirCont.addSubDir(shortName); - execute(subDir); //recurse + const auto devId = static_cast(readNumberC()); // + const auto fId = static_cast(readNumberC()); //silence "loss of precision" compiler warnings + return std::make_pair(devId, fId); } -}; -namespace -{ -typedef std::string UniqueId; -typedef std::shared_ptr > MemoryStreamPtr; //byte stream representing DirInformation -typedef std::map 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(); + while (readNumberC()) //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(); + const std::uint64_t fileSize = readNumberC(); + const FileId fileID = readFileId(); - streamList.clear(); + dirCont.addSubFile(shortName, + FileDescriptor(modTime, fileSize, fileID)); + } - boost::uint32_t dbCount = readNumberC(); //number of databases: one for each sync-pair - while (dbCount-- != 0) + while (readNumberC()) //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(); + const Zstring targetPath = readStringUtf8(); //file name + const LinkDescriptor::LinkType linkType = static_cast(readNumberC()); + + dirCont.addSubLink(shortName, + LinkDescriptor(modTime, targetPath, linkType)); + } - streamList.insert(std::make_pair(sessionID, buffer)); + while (readNumberC()) //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& stream, const Zstring& fileName) //throw FileError -> return value always bound! -{ - try - { - //read streams into DirInfo - auto dirInfo = std::make_shared(); - 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 zen::loadFromDisk(const BaseDirMapping& baseMapping) //throw FileError -{ - const Zstring fileNameLeft = getDBFilename(baseMapping); - const Zstring fileNameRight = getDBFilename(baseMapping); + //save file format version + cw.writeNumberC(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(static_cast(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(iter->first ); //sync session id + cw.writeStringC(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 -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(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(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(false); //mark last entry } + void writeStringUtf8(const Zstring& str) { writeStringC(utf8CvrtTo>(str)); } + + void writeFileId(const FileId& id) + { + writeNumberC(id.first ); //device id + writeNumberC(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()) { writeNumberC(true); //mark beginning of entry - writeStringC(fileMap.getShortName()); //save respecting case! (Windows) - writeNumberC(to(fileMap.getLastWriteTime())); //last modification time - writeNumberC(to(fileMap.getFileSize())); //filesize + writeStringUtf8(fileMap.getShortName()); //save respecting case! (Windows) + writeNumberC(to(fileMap.getLastWriteTime())); + writeNumberC(to(fileMap.getFileSize())); + writeFileId(fileMap.getFileId()); } } else //not in sync: reuse last synchronous state @@ -320,9 +284,10 @@ private: if (iter != oldParentDir->files.end()) { writeNumberC(true); //mark beginning of entry - writeStringC(iter->first); //save respecting case! (Windows) - writeNumberC(to(iter->second.lastWriteTimeRaw)); //last modification time - writeNumberC(to(iter->second.fileSize)); //filesize + writeStringUtf8(iter->first); //save respecting case! (Windows) + writeNumberC(to(iter->second.lastWriteTimeRaw)); + writeNumberC(to(iter->second.fileSize)); + writeFileId(iter->second.id); } } } @@ -335,9 +300,9 @@ private: if (!linkObj.isEmpty()) { writeNumberC(true); //mark beginning of entry - writeStringC(linkObj.getShortName()); //save respecting case! (Windows) - writeNumberC(to(linkObj.getLastWriteTime())); //last modification time - writeStringC(linkObj.getTargetPath()); + writeStringUtf8(linkObj.getShortName()); //save respecting case! (Windows) + writeNumberC(to(linkObj.getLastWriteTime())); + writeStringUtf8(linkObj.getTargetPath()); writeNumberC(linkObj.getLinkType()); } } @@ -349,9 +314,9 @@ private: if (iter != oldParentDir->links.end()) { writeNumberC(true); //mark beginning of entry - writeStringC(iter->first); //save respecting case! (Windows) - writeNumberC(to(iter->second.lastWriteTimeRaw)); //last modification time - writeStringC(iter->second.targetPath); + writeStringUtf8(iter->first); //save respecting case! (Windows) + writeNumberC(to(iter->second.lastWriteTimeRaw)); + writeStringUtf8(iter->second.targetPath); writeNumberC(iter->second.type); } } @@ -379,8 +344,8 @@ private: if (!dirMap.isEmpty()) { writeNumberC(true); //mark beginning of entry - writeStringC(dirMap.getShortName()); //save respecting case! (Windows) - execute(dirMap, oldDir); //recurse + writeStringUtf8(dirMap.getShortName()); //save respecting case! (Windows) + recurse(dirMap, oldDir); } } else //not in sync: reuse last synchronous state @@ -388,8 +353,8 @@ private: if (oldDir) { writeNumberC(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(true); - writeStringC(dirMap.getShortName()); + writeStringUtf8(dirMap.getShortName()); //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 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 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(FILE_FORMAT_VER); + const Zstring fileNameLeft = getDBFilename(baseMapping); + const Zstring fileNameRight = getDBFilename(baseMapping); - writeNumberC(static_cast(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(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>(); - { - wxMemoryOutputStream buffer; - const DirContainer* oldDir = oldDirInfoLeft.get() ? &oldDirInfoLeft->baseDirContainer : NULL; - SaveDirInfo(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>(); - { - wxMemoryOutputStream buffer; - const DirContainer* oldDir = oldDirInfoRight.get() ? &oldDirInfoRight->baseDirContainer : NULL; - SaveDirInfo(baseMapping, oldDir, dbNameRight, buffer); - newStreamRight->resize(buffer.GetSize()); //convert output stream to char-array - buffer.CopyTo(&(*newStreamRight)[0], buffer.GetSize()); // - } + MemoryStream newStreamLeft = StreamGenerator::execute(baseMapping, oldDirInfoLeft .get() ? &oldDirInfoLeft ->baseDirContainer : NULL, dbNameLeft); + MemoryStream newStreamRight = StreamGenerator::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: -- cgit