summaryrefslogtreecommitdiff
path: root/zen/serialize.h
diff options
context:
space:
mode:
Diffstat (limited to 'zen/serialize.h')
-rwxr-xr-x[-rw-r--r--]zen/serialize.h607
1 files changed, 280 insertions, 327 deletions
diff --git a/zen/serialize.h b/zen/serialize.h
index 290d9200..bb2f7a45 100644..100755
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -1,327 +1,280 @@
-// *****************************************************************************
-// * 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 freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef SERIALIZE_H_839405783574356
-#define SERIALIZE_H_839405783574356
-
-#include <functional>
-#include <cstdint>
-#include "string_base.h"
-//keep header clean from specific stream implementations! (e.g.file_io.h)! used by abstract.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<char>, std::string, Zbase<char>)
-*/
-
-//binary container reference implementations
-using Utf8String = Zbase<char>; //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<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite
-{
-public:
- using value_type = std::vector<char>::value_type;
- using iterator = std::vector<char>::iterator;
- using const_iterator = std::vector<char>::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<std::vector<char>> buffer { std::make_shared<std::vector<char>>() }; //always bound!
- //perf: shared_ptr indirection irrelevant: less than 1% slower!
-};
-
-/*
----------------------------------
-|Unbuffered Input Stream Concept|
----------------------------------
-struct UnbufferedInputStream
-{
- size_t getBlockSize();
- size_t tryRead(void* buffer, size_t bytesToRead); //may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
-};
-
-----------------------------------
-|Unbuffered Output Stream Concept|
-----------------------------------
-struct UnbufferedOutputStream
-{
- size_t getBlockSize();
- size_t tryWrite(const void* buffer, size_t bytesToWrite); //may return short! CONTRACT: bytesToWrite > 0
-};
-*/
-
-//functions based on unbuffered stream abstraction
-template <class UnbufferedInputStream, class UnbufferedOutputStream>
-void unbufferedStreamCopy(UnbufferedInputStream& streamIn, UnbufferedOutputStream& streamOut, const std::function<void(std::int64_t bytesDelta)>& notifyProgress); //throw X
-
-template <class BinContainer, class UnbufferedOutputStream>
-void unbufferedSave(const BinContainer& buffer, UnbufferedOutputStream& streamOut, const std::function<void(std::int64_t bytesDelta)>& notifyProgress); //throw X
-
-template <class BinContainer, class UnbufferedInputStream>
-BinContainer unbufferedLoad(UnbufferedInputStream& streamIn, const std::function<void(std::int64_t bytesDelta)>& notifyProgress); //throw X
-
-/*
--------------------------------
-|Buffered Input Stream Concept|
--------------------------------
-struct BufferedInputStream
-{
- size_t read(void* buffer, size_t bytesToRead); //return "len" bytes unless end of stream! throw ?
-};
-
---------------------------------
-|Buffered Output Stream Concept|
---------------------------------
-struct BufferedOutputStream
-{
- void write(const void* buffer, size_t bytesToWrite); //throw ?
-};
-*/
-//functions based on buffered stream abstraction
-template <class N, class BufferedOutputStream> void writeNumber (BufferedOutputStream& stream, const N& num); //
-template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //throw ()
-template < class BufferedOutputStream> void writeArray (BufferedOutputStream& stream, const void* data, size_t len); //
-//----------------------------------------------------------------------
-class UnexpectedEndOfStreamError {};
-template <class N, class BufferedInputStream> N readNumber (BufferedInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data)
-template <class C, class BufferedInputStream> C readContainer(BufferedInputStream& stream); //
-template < class BufferedInputStream> void readArray (BufferedInputStream& stream, void* data, size_t len); //
-
-//buffered input/output stream reference implementations:
-template <class BinContainer>
-struct MemoryStreamIn
-{
- MemoryStreamIn(const BinContainer& cont) : buffer_(cont) {} //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<char*>(data));
- pos_ += bytesRead;
- return bytesRead;
- }
-
- size_t pos() const { return pos_; }
-
-private:
- const BinContainer buffer_;
- size_t pos_ = 0;
-};
-
-template <class BinContainer>
-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<const char*>(data), static_cast<const char*>(data) + len, buffer_.begin() + oldSize);
- }
-
- const BinContainer& ref() const { return buffer_; }
-
-private:
- BinContainer buffer_;
-};
-
-
-
-
-
-
-
-
-//-----------------------implementation-------------------------------
-template <class UnbufferedInputStream, class UnbufferedOutputStream> inline
-void unbufferedStreamCopy(UnbufferedInputStream& streamIn, //throw X
- UnbufferedOutputStream& streamOut, //
- const std::function<void(std::int64_t bytesDelta)>& notifyProgress) //optional
-{
- size_t unevenBytes = 0;
- auto reportBytesProcessed = [&](size_t bytesReadOrWritten)
- {
- if (notifyProgress)
- {
- const size_t bytesToReport = (unevenBytes + bytesReadOrWritten) / 2;
- notifyProgress(bytesToReport); //throw X!
- unevenBytes = (unevenBytes + bytesReadOrWritten) - bytesToReport * 2; //unsigned arithmetics!
- }
- };
-
- const size_t blockSizeIn = streamIn .getBlockSize();
- const size_t blockSizeOut = streamOut.getBlockSize();
- if (blockSizeIn == 0 || blockSizeOut == 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
- std::vector<char> buffer;
- for (;;)
- {
- buffer.resize(buffer.size() + blockSizeIn);
- const size_t bytesRead = streamIn.tryRead(&*(buffer.end() - blockSizeIn), blockSizeIn); //throw X; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
- buffer.resize(buffer.size() - blockSizeIn + bytesRead); //caveat: unsigned arithmetics
-
- reportBytesProcessed(bytesRead); //throw X!
-
- size_t bytesRemaining = buffer.size();
- while (bytesRemaining >= blockSizeOut)
- {
- const size_t bytesWritten = streamOut.tryWrite(&*(buffer.end() - bytesRemaining), blockSizeOut); //throw X; may return short! CONTRACT: bytesToWrite > 0
- bytesRemaining -= bytesWritten;
- reportBytesProcessed(bytesWritten); //throw X!
- }
- buffer.erase(buffer.begin(), buffer.end() - bytesRemaining);
-
- if (bytesRead == 0) //end of file
- break;
- }
-
- for (size_t bytesRemaining = buffer.size(); bytesRemaining > 0;)
- {
- const size_t bytesWritten = streamOut.tryWrite(&*(buffer.end() - bytesRemaining), bytesRemaining); //throw X; may return short! CONTRACT: bytesToWrite > 0
- bytesRemaining -= bytesWritten;
- reportBytesProcessed(bytesWritten); //throw X!
- }
-
- if (unevenBytes != 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-}
-
-
-template <class BinContainer, class UnbufferedOutputStream> inline
-void unbufferedSave(const BinContainer& buffer,
- UnbufferedOutputStream& streamOut, //throw X
- const std::function<void(std::int64_t bytesDelta)>& notifyProgress) //optional
-{
- const size_t blockSize = streamOut.getBlockSize();
- if (blockSize == 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
-
- for (size_t bytesRemaining = buffer.size(); bytesRemaining > 0;)
- {
- const size_t bytesToWrite = std::min(bytesRemaining, blockSize);
- const size_t bytesWritten = streamOut.tryWrite(&*(buffer.end() - bytesRemaining), bytesToWrite); //throw X; may return short! CONTRACT: bytesToWrite > 0
- bytesRemaining -= bytesWritten;
- if (notifyProgress) notifyProgress(bytesWritten); //throw X!
- }
-}
-
-
-template <class BinContainer, class UnbufferedInputStream> inline
-BinContainer unbufferedLoad(UnbufferedInputStream& streamIn, //throw X
- const std::function<void(std::int64_t bytesDelta)>& notifyProgress) //optional
-{
- const size_t blockSize = streamIn.getBlockSize();
- if (blockSize == 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
-
- BinContainer buffer;
- for (;;)
- {
- buffer.resize(buffer.size() + blockSize);
- const size_t bytesRead = streamIn.tryRead(&*(buffer.end() - blockSize), blockSize); //throw X; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
- buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
-
- if (notifyProgress) notifyProgress(bytesRead); //throw X!
-
- if (bytesRead == 0) //end of file
- return buffer;
- }
-}
-
-
-template <class BufferedOutputStream> inline
-void writeArray(BufferedOutputStream& stream, const void* data, size_t len)
-{
- stream.write(data, len);
-}
-
-
-template <class N, class BufferedOutputStream> inline
-void writeNumber(BufferedOutputStream& stream, const N& num)
-{
- static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "not a number!");
- writeArray(stream, &num, sizeof(N));
-}
-
-
-template <class C, class BufferedOutputStream> inline
-void writeContainer(BufferedOutputStream& 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<std::uint32_t>(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 <class BufferedInputStream> inline
-void readArray(BufferedInputStream& stream, void* data, size_t len) //throw UnexpectedEndOfStreamError
-{
- const size_t bytesRead = stream.read(data, len);
- if (bytesRead < len)
- throw UnexpectedEndOfStreamError();
-}
-
-
-template <class N, class BufferedInputStream> inline
-N readNumber(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
-{
- static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "");
- N num = 0;
- readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError
- return num;
-}
-
-
-template <class C, class BufferedInputStream> inline
-C readContainer(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
-{
- C cont;
- auto strLength = readNumber<std::uint32_t>(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_839405783574356
+// *****************************************************************************
+// * 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 freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef SERIALIZE_H_839405783574356
+#define SERIALIZE_H_839405783574356
+
+#include <functional>
+#include <cstdint>
+#include "string_base.h"
+//keep header clean from specific stream implementations! (e.g.file_io.h)! used by abstract.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<char>, std::string, Zbase<char>)
+*/
+
+//binary container reference implementations
+using Utf8String = Zbase<char>; //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<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite
+{
+public:
+ using value_type = std::vector<char>::value_type;
+ using iterator = std::vector<char>::iterator;
+ using const_iterator = std::vector<char>::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<std::vector<char>> buffer { std::make_shared<std::vector<char>>() }; //always bound!
+ //perf: shared_ptr indirection irrelevant: less than 1% slower!
+};
+
+/*
+-------------------------------
+|Buffered Input Stream Concept|
+-------------------------------
+struct BufferedInputStream
+{
+ size_t read(void* buffer, size_t bytesToRead); //throw X; return "bytesToRead" bytes unless end of stream!
+
+Optional: support stream-copying
+--------------------------------
+ size_t getBlockSize() const;
+ const IOCallback& notifyUnbufferedIO
+};
+
+--------------------------------
+|Buffered Output Stream Concept|
+--------------------------------
+struct BufferedOutputStream
+{
+ void write(const void* buffer, size_t bytesToWrite); //throw X
+
+Optional: support stream-copying
+--------------------------------
+ const IOCallback& notifyUnbufferedIO
+};
+*/
+using IOCallback = std::function<void(int64_t bytesDelta)>; //throw X
+
+
+//functions based on buffered stream abstraction
+template <class BufferedInputStream, class BufferedOutputStream>
+void bufferedStreamCopy(BufferedInputStream& streamIn, BufferedOutputStream& streamOut); //throw X
+
+template <class BinContainer, class BufferedInputStream> BinContainer
+bufferedLoad(BufferedInputStream& streamIn); //throw X
+
+template <class N, class BufferedOutputStream> void writeNumber (BufferedOutputStream& stream, const N& num); //
+template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //throw ()
+template < class BufferedOutputStream> void writeArray (BufferedOutputStream& stream, const void* buffer, size_t len); //
+//----------------------------------------------------------------------
+class UnexpectedEndOfStreamError {};
+template <class N, class BufferedInputStream> N readNumber (BufferedInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data)
+template <class C, class BufferedInputStream> C readContainer(BufferedInputStream& stream); //
+template < class BufferedInputStream> void readArray (BufferedInputStream& stream, void* buffer, size_t len); //
+
+
+struct IOCallbackDivider
+{
+ IOCallbackDivider(const IOCallback& notifyUnbufferedIO, int64_t& totalUnbufferedIO) : totalUnbufferedIO_(totalUnbufferedIO), notifyUnbufferedIO_(notifyUnbufferedIO) {}
+
+ void operator()(int64_t bytesDelta)
+ {
+ if (notifyUnbufferedIO_) notifyUnbufferedIO_((totalUnbufferedIO_ - totalUnbufferedIO_ / 2 * 2 + bytesDelta) / 2); //throw X!
+ totalUnbufferedIO_ += bytesDelta;
+ }
+
+private:
+ int64_t& totalUnbufferedIO_;
+ const IOCallback& notifyUnbufferedIO_;
+};
+
+//buffered input/output stream reference implementations:
+template <class BinContainer>
+struct MemoryStreamIn
+{
+ MemoryStreamIn(const BinContainer& cont) : buffer_(cont) {} //this better be cheap!
+
+ size_t read(void* buffer, size_t bytesToRead) //return "bytesToRead" bytes unless end of stream!
+ {
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+ const size_t bytesRead = std::min(bytesToRead, buffer_.size() - pos_);
+ auto itFirst = buffer_.begin() + pos_;
+ std::copy(itFirst, itFirst + bytesRead, static_cast<char*>(buffer));
+ pos_ += bytesRead;
+ return bytesRead;
+ }
+
+ size_t pos() const { return pos_; }
+
+private:
+ MemoryStreamIn (const MemoryStreamIn&) = delete;
+ MemoryStreamIn& operator=(const MemoryStreamIn&) = delete;
+
+ const BinContainer buffer_;
+ size_t pos_ = 0;
+};
+
+template <class BinContainer>
+struct MemoryStreamOut
+{
+ MemoryStreamOut() = default;
+
+ void write(const void* buffer, size_t bytesToWrite)
+ {
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+ const size_t oldSize = buffer_.size();
+ buffer_.resize(oldSize + bytesToWrite);
+ std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.begin() + oldSize);
+ }
+
+ const BinContainer& ref() const { return buffer_; }
+
+private:
+ MemoryStreamOut (const MemoryStreamOut&) = delete;
+ MemoryStreamOut& operator=(const MemoryStreamOut&) = delete;
+
+ BinContainer buffer_;
+};
+
+
+
+
+
+
+
+
+//-----------------------implementation-------------------------------
+template <class BufferedInputStream, class BufferedOutputStream> inline
+void bufferedStreamCopy(BufferedInputStream& streamIn, //throw X
+ BufferedOutputStream& streamOut) //
+{
+ const size_t blockSize = streamIn.getBlockSize();
+ if (blockSize == 0)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ std::vector<char> buffer(blockSize);
+ for (;;)
+ {
+ const size_t bytesRead = streamIn.read(&buffer[0], blockSize); //throw X; return "bytesToRead" bytes unless end of stream!
+ streamOut.write(&buffer[0], bytesRead); //throw X
+
+ if (bytesRead < blockSize) //end of file
+ break;
+ }
+}
+
+
+template <class BinContainer, class BufferedInputStream> inline
+BinContainer bufferedLoad(BufferedInputStream& streamIn) //throw X
+{
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+
+ const size_t blockSize = streamIn.getBlockSize();
+ if (blockSize == 0)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ BinContainer buffer;
+ for (;;)
+ {
+ buffer.resize(buffer.size() + blockSize);
+ const size_t bytesRead = streamIn.read(&*(buffer.end() - blockSize), blockSize); //throw X; return "bytesToRead" bytes unless end of stream!
+ buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
+
+ if (bytesRead < blockSize) //end of file
+ return buffer;
+ }
+}
+
+
+template <class BufferedOutputStream> inline
+void writeArray(BufferedOutputStream& stream, const void* buffer, size_t len)
+{
+ stream.write(buffer, len);
+}
+
+
+template <class N, class BufferedOutputStream> inline
+void writeNumber(BufferedOutputStream& stream, const N& num)
+{
+ static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "not a number!");
+ writeArray(stream, &num, sizeof(N));
+}
+
+
+template <class C, class BufferedOutputStream> inline
+void writeContainer(BufferedOutputStream& 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<uint32_t>(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 <class BufferedInputStream> inline
+void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw UnexpectedEndOfStreamError
+{
+ const size_t bytesRead = stream.read(buffer, len);
+ if (bytesRead < len)
+ throw UnexpectedEndOfStreamError();
+}
+
+
+template <class N, class BufferedInputStream> inline
+N readNumber(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
+{
+ static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "");
+ N num = 0;
+ readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError
+ return num;
+}
+
+
+template <class C, class BufferedInputStream> inline
+C readContainer(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
+{
+ C cont;
+ auto strLength = readNumber<uint32_t>(stream); //throw UnexpectedEndOfStreamError
+ 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_839405783574356
bgstack15