// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 * // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** #ifndef SERIALIZE_H_INCLUDED_83940578357 #define SERIALIZE_H_INCLUDED_83940578357 #include #include #include "string_base.h" #include "file_io.h" namespace zen { //high-performance unformatted serialization (avoiding wxMemoryOutputStream/wxMemoryInputStream inefficiencies) /* -------------------------- |Binary Container Concept| -------------------------- binary container for data storage: must support "basic" std::vector interface (e.g. std::vector, std::string, Zbase) */ //binary container reference implementations typedef Zbase Utf8String; //ref-counted + COW text stream + guaranteed performance: exponential growth class ByteArray; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?) class ByteArray //essentially a std::vector with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite { public: ByteArray() : buffer(std::make_shared>()) {} typedef std::vector::value_type value_type; typedef std::vector::iterator iterator; typedef std::vector::const_iterator const_iterator; iterator begin() { return buffer->begin(); } iterator end () { return buffer->end (); } const_iterator begin() const { return buffer->begin(); } const_iterator end () const { return buffer->end (); } void resize(size_t len) { buffer->resize(len); } size_t size() const { return buffer->size(); } bool empty() const { return buffer->empty(); } inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer == *rhs.buffer; } private: std::shared_ptr> buffer; //always bound! //perf: shared_ptr indirection irrelevant: less than 1% slower! }; //---------------------------------------------------------------------- //functions based on binary container abstraction template void saveBinStream(const Zstring& filepath, const BinContainer& cont, const std::function& onUpdateStatus); //throw FileError template BinContainer loadBinStream(const Zstring& filepath, const std::function& onUpdateStatus); //throw FileError /* ----------------------------- |Binary Input Stream Concept| ----------------------------- struct BinInputStream { size_t read(void* data, size_t len); //return "len" bytes unless end of stream! }; ------------------------------ |Binary Output Stream Concept| ------------------------------ struct BinOutputStream { void write(const void* data, size_t len); }; */ //binary input/output stream reference implementations: template struct MemoryStreamIn { MemoryStreamIn(const BinContainer& cont) : buffer(cont), pos(0) {} //this better be cheap! size_t read(void* data, size_t len) //return "len" bytes unless end of stream! { static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes const size_t bytesRead = std::min(len, buffer.size() - pos); auto itFirst = buffer.begin() + pos; std::copy(itFirst, itFirst + bytesRead, static_cast(data)); pos += bytesRead; return bytesRead; } private: const BinContainer buffer; size_t pos; }; template struct MemoryStreamOut { void write(const void* data, size_t len) { static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes const size_t oldSize = buffer.size(); buffer.resize(oldSize + len); std::copy(static_cast(data), static_cast(data) + len, buffer.begin() + oldSize); } const BinContainer& ref() const { return buffer; } private: BinContainer buffer; }; //---------------------------------------------------------------------- //functions based on binary stream abstraction template void copyStream(BinInputStream& streamIn, BinOutputStream& streamOut, const std::function& onNotifyCopyStatus); //optional template void writeNumber (BinOutputStream& stream, const N& num); // template void writeContainer(BinOutputStream& stream, const C& str); //throw () template < class BinOutputStream> void writeArray (BinOutputStream& stream, const void* data, size_t len); // //---------------------------------------------------------------------- class UnexpectedEndOfStreamError {}; template N readNumber (BinInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data) template C readContainer(BinInputStream& stream); // template < class BinInputStream> void readArray (BinInputStream& stream, void* data, size_t len); // //-----------------------implementation------------------------------- template inline void copyStream(BinInputStream& streamIn, BinOutputStream& streamOut, size_t blockSize, const std::function& onNotifyCopyStatus) //optional { assert(blockSize > 0); std::vector buffer(blockSize); for (;;) { const size_t bytesRead = streamIn.read(&buffer[0], buffer.size()); streamOut.write(&buffer[0], bytesRead); if (onNotifyCopyStatus) onNotifyCopyStatus(bytesRead); //throw X! if (bytesRead != buffer.size()) //end of file break; } } template inline void saveBinStream(const Zstring& filepath, //throw FileError const BinContainer& cont, const std::function& onUpdateStatus) //optional { MemoryStreamIn streamIn(cont); FileOutput streamOut(filepath, zen::FileOutput::ACC_OVERWRITE); //throw FileError, (ErrorTargetExisting) if (onUpdateStatus) onUpdateStatus(0); //throw X! copyStream(streamIn, streamOut, streamOut.optimalBlockSize(), onUpdateStatus); //throw FileError } template inline BinContainer loadBinStream(const Zstring& filepath, //throw FileError const std::function& onUpdateStatus) //optional { FileInput streamIn(filepath); //throw FileError, ErrorFileLocked if (onUpdateStatus) onUpdateStatus(0); //throw X! MemoryStreamOut streamOut; copyStream(streamIn, streamOut, streamIn.optimalBlockSize(), onUpdateStatus); //throw FileError return streamOut.ref(); } template inline void writeArray(BinOutputStream& stream, const void* data, size_t len) { stream.write(data, len); } template inline void writeNumber(BinOutputStream& stream, const N& num) { static_assert(IsArithmetic::value || IsSameType::value, ""); writeArray(stream, &num, sizeof(N)); } template inline void writeContainer(BinOutputStream& stream, const C& cont) //don't even consider UTF8 conversions here, we're handling arbitrary binary data! { const auto len = cont.size(); writeNumber(stream, static_cast(len)); if (len > 0) writeArray(stream, &*cont.begin(), sizeof(typename C::value_type) * len); //don't use c_str(), but access uniformly via STL interface } template inline void readArray(BinInputStream& stream, void* data, size_t len) //throw UnexpectedEndOfStreamError { const size_t bytesRead = stream.read(data, len); if (bytesRead < len) throw UnexpectedEndOfStreamError(); } template inline N readNumber(BinInputStream& stream) //throw UnexpectedEndOfStreamError { static_assert(IsArithmetic::value || IsSameType::value, ""); N num = 0; readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError return num; } template inline C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError { C cont; auto strLength = readNumber(stream); if (strLength > 0) { try { cont.resize(strLength); //throw std::bad_alloc } catch (std::bad_alloc&) //most likely this is due to data corruption! { throw UnexpectedEndOfStreamError(); } readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError } return cont; } } #endif //SERIALIZE_H_INCLUDED_83940578357