Extend Binary{Reader,Writer} to ease binary (de)serialization

To implement reflection-enabled binary (de)serialization
in https://github.com/Martchus/reflective-rapidjson.
This commit is contained in:
Martchus 2018-05-31 23:26:38 +02:00
parent b77607f3e0
commit 5deb077fe5
5 changed files with 336 additions and 21 deletions

View File

@ -136,8 +136,8 @@ set(META_APP_URL "https://github.com/${META_APP_AUTHOR}/${META_PROJECT_NAME}")
set(META_APP_DESCRIPTION "Useful C++ classes and routines such as argument parser, IO and conversion utilities")
set(META_FEATURES_FOR_COMPILER_DETECTION_HEADER cxx_thread_local)
set(META_VERSION_MAJOR 4)
set(META_VERSION_MINOR 14)
set(META_VERSION_PATCH 2)
set(META_VERSION_MINOR 15)
set(META_VERSION_PATCH 0)
# find required 3rd party libraries
include(3rdParty)

View File

@ -19,6 +19,7 @@ using namespace ConversionUtilities;
* \class IoUtilities::BinaryReader
* \brief Reads primitive data types from a std::istream.
* \remarks Supports both, little endian and big endian.
* \sa For automatic serialization of structs, see https://github.com/Martchus/reflective-rapidjson.
*/
/*!
@ -92,13 +93,7 @@ istream::pos_type BinaryReader::readStreamsize()
return streamsize;
}
/*!
* \brief Reads a length prefixed string from the current stream.
*
* \remarks Reads the length prefix from the stream and then a string of the denoted length.
* Advances the current position of the stream by the denoted length of the string plus the prefix length.
*/
string BinaryReader::readLengthPrefixedString()
void BinaryReader::bufferVariableLengthInteger()
{
static constexpr int maxPrefixLength = 8;
int prefixLength = 1;
@ -109,12 +104,22 @@ string BinaryReader::readLengthPrefixedString()
mask >>= 1;
}
if (prefixLength > maxPrefixLength) {
throw ConversionException("Length denotation of length-prefixed string exceeds maximum.");
throw ConversionException("Length denotation of variable length unsigned integer exceeds maximum.");
}
memset(m_buffer, 0, maxPrefixLength);
m_stream->read(m_buffer + (maxPrefixLength - prefixLength), prefixLength);
*(m_buffer + (maxPrefixLength - prefixLength)) ^= mask;
return readString(BE::toUInt64(m_buffer));
}
/*!
* \brief Reads a length prefixed string from the current stream.
* \remarks Reads the length prefix from the stream and then a string of the denoted length.
* Advances the current position of the stream by the denoted length of the string plus the prefix length.
* \todo Make inline in v5.
*/
string BinaryReader::readLengthPrefixedString()
{
return readString(readVariableLengthUIntBE());
}
/*!
@ -142,7 +147,7 @@ string BinaryReader::readTerminatedString(byte termination)
{
stringstream ss(ios_base::in | ios_base::out | ios_base::binary);
ss.exceptions(ios_base::badbit | ios_base::failbit);
m_stream->get(*ss.rdbuf(), termination); // delim byte is not extracted from the stream
m_stream->get(*ss.rdbuf(), static_cast<char>(termination)); // delim byte is not extracted from the stream
m_stream->seekg(1, ios_base::cur); // "extract" delim byte manually
return ss.str();
}

View File

@ -41,6 +41,7 @@ public:
uint64 readUInt56BE();
int64 readInt64BE();
uint64 readUInt64BE();
uint64 readVariableLengthUIntBE();
float32 readFloat32BE();
float64 readFloat64BE();
int16 readInt16LE();
@ -55,6 +56,7 @@ public:
uint64 readUInt56LE();
int64 readInt64LE();
uint64 readUInt64LE();
uint64 readVariableLengthUIntLE();
float32 readFloat32LE();
float64 readFloat64LE();
char readChar();
@ -78,7 +80,23 @@ public:
static uint32 computeCrc32(const char *buffer, std::size_t length);
static const uint32 crc32Table[];
// declare further overloads for read() to ease use of BinaryReader in templates
void read(char &oneCharacter);
void read(byte &oneByte);
void read(bool &oneBool);
void read(std::string &lengthPrefixedString);
void read(int16 &one16BitInt);
void read(uint16 &one16BitUInt);
void read(int32 &one32BitInt);
void read(uint32 &one32BitUInt);
void read(int64 &one64BitInt);
void read(uint64 &one64BitUInt);
void read(float32 &one32BitFloat);
void read(float64 &one64BitFloat);
private:
void bufferVariableLengthInteger();
std::istream *m_stream;
bool m_ownership;
char m_buffer[8];
@ -317,6 +335,16 @@ inline uint64 BinaryReader::readUInt64BE()
return ConversionUtilities::BE::toUInt64(m_buffer);
}
/*!
* \brief Reads an up to 8 byte long big endian unsigned integer from the current stream and advances the current position of the stream by one to eight byte.
* \throws Throws ConversionException if the size of the integer exceeds the maximum.
*/
inline uint64 BinaryReader::readVariableLengthUIntBE()
{
bufferVariableLengthInteger();
return ConversionUtilities::BE::toUInt64(m_buffer);
}
/*!
* \brief Reads a 32-bit big endian floating point value from the current stream and advances the current position of the stream by four bytes.
*/
@ -461,6 +489,16 @@ inline uint64 BinaryReader::readUInt64LE()
return ConversionUtilities::LE::toUInt64(m_buffer);
}
/*!
* \brief Reads an up to 8 byte long little endian unsigned integer from the current stream and advances the current position of the stream by one to eight byte.
* \throws Throws ConversionException if the size of the integer exceeds the maximum.
*/
inline uint64 BinaryReader::readVariableLengthUIntLE()
{
bufferVariableLengthInteger();
return ConversionUtilities::LE::toUInt64(m_buffer);
}
/*!
* \brief Reads a 32-bit little endian floating point value from the current stream and advances the current position of the stream by four bytes.
*/
@ -557,6 +595,106 @@ inline float32 BinaryReader::readFixed16LE()
{
return ConversionUtilities::toFloat32(readUInt32LE());
}
/*!
* \brief Reads a single character from the current stream and advances the current position of the stream by one byte.
*/
inline void BinaryReader::read(char &oneCharacter)
{
oneCharacter = readChar();
}
/*!
* \brief Reads a single byte/unsigned character from the current stream and advances the current position of the stream by one byte.
*/
inline void BinaryReader::read(byte &oneByte)
{
oneByte = readByte();
}
/*!
* \brief Reads a boolean value from the current stream and advances the current position of the stream by one byte.
* \sa IoUtilities::BitReader
*/
inline void BinaryReader::read(bool &oneBool)
{
oneBool = readBool();
}
/*!
* \brief Reads a length prefixed string from the current stream.
*
* \remarks Reads the length prefix from the stream and then a string of the denoted length.
* Advances the current position of the stream by the denoted length of the string plus the prefix length.
*/
inline void BinaryReader::read(std::string &lengthPrefixedString)
{
lengthPrefixedString = readLengthPrefixedString();
}
/*!
* \brief Reads a 16-bit big endian signed integer from the current stream and advances the current position of the stream by two bytes.
*/
inline void BinaryReader::read(int16 &one16BitInt)
{
one16BitInt = readInt16BE();
}
/*!
* \brief Reads a 16-bit big endian unsigned integer from the current stream and advances the current position of the stream by two bytes.
*/
inline void BinaryReader::read(uint16 &one16BitUInt)
{
one16BitUInt = readUInt16BE();
}
/*!
* \brief Reads a 16-bit big endian signed integer from the current stream and advances the current position of the stream by two bytes.
*/
inline void BinaryReader::read(int32 &one32BitInt)
{
one32BitInt = readInt16BE();
}
/*!
* \brief Reads a 32-bit big endian unsigned integer from the current stream and advances the current position of the stream by four bytes.
*/
inline void BinaryReader::read(uint32 &one32BitUInt)
{
one32BitUInt = readUInt32BE();
}
/*!
* \brief Reads a 64-bit big endian signed integer from the current stream and advances the current position of the stream by eight bytes.
*/
inline void BinaryReader::read(int64 &one64BitInt)
{
one64BitInt = readInt64BE();
}
/*!
* \brief Reads a 64-bit big endian unsigned integer from the current stream and advances the current position of the stream by eight bytes.
*/
inline void BinaryReader::read(uint64 &one64BitUInt)
{
one64BitUInt = readUInt64BE();
}
/*!
* \brief Reads a 32-bit big endian floating point value from the current stream and advances the current position of the stream by four bytes.
*/
inline void BinaryReader::read(float32 &one32BitFloat)
{
one32BitFloat = readFloat32BE();
}
/*!
* \brief Reads a 64-bit big endian floating point value from the current stream and advances the current position of the stream by eight bytes.
*/
inline void BinaryReader::read(float64 &one64BitFloat)
{
one64BitFloat = readFloat64BE();
}
} // namespace IoUtilities
#endif // IOUTILITIES_BINERYREADER_H

View File

@ -13,6 +13,7 @@ using namespace ConversionUtilities;
* \class IoUtilities::BinaryWriter
* \brief Writes primitive data types to a std::ostream.
* \remarks Supports both, little endian and big endian.
* \sa For automatic deserialization of structs, see https://github.com/Martchus/reflective-rapidjson.
*/
/*!
@ -71,24 +72,48 @@ void BinaryWriter::setStream(ostream *stream, bool giveOwnership)
}
/*!
* \brief Writes the length of a string and the string itself to the current stream.
*
* Advances the current position of the stream by the length of the string plus the size of the length prefix.
* \brief Writes the specified integer \a value. Conversion to bytes is done using the specified function.
*/
void BinaryWriter::writeLengthPrefixedString(const string &value)
void BinaryWriter::writeVariableLengthInteger(uint64 value, void (*getBytes)(uint64, char *))
{
const uint64 size = value.size();
uint64 boundCheck = 0x80;
byte prefixLength = 1;
for (; boundCheck != 0x8000000000000000; boundCheck <<= 7, ++prefixLength) {
if (size < boundCheck) {
BE::getBytes(size | boundCheck, m_buffer);
if (value < boundCheck) {
getBytes(value | boundCheck, m_buffer);
break;
}
}
if (prefixLength == 9) {
throw ConversionException("The size of the string exceeds the maximum.");
throw ConversionException("The variable-length integer to be written exceeds the maximum.");
}
m_stream->write(m_buffer + 8 - prefixLength, prefixLength);
m_stream->write(value.data(), static_cast<streamsize>(size));
}
/*!
* \brief Writes the length of a string and the string itself to the current stream.
*
* Advances the current position of the stream by the length of the string plus the size of the length prefix.
*
* \throws Throws ConversionException if the string size exceeds the maximum.
* \todo Make inline in v5.
*/
void BinaryWriter::writeLengthPrefixedString(const string &value)
{
writeVariableLengthUIntBE(value.size());
m_stream->write(value.data(), static_cast<streamsize>(value.size()));
}
/*!
* \brief Writes the length of a string and the string itself to the current stream.
*
* Advances the current position of the stream by the length of the string plus the size of the length prefix.
*
* \throws Throws ConversionException if the string size exceeds the maximum.
* \todo Make inline in v5.
*/
void BinaryWriter::writeLengthPrefixedCString(const char *value, size_t size)
{
writeVariableLengthUIntBE(size);
m_stream->write(value, static_cast<streamsize>(size));
}

View File

@ -4,6 +4,7 @@
#include "../conversion/binaryconversion.h"
#include "../conversion/types.h"
#include <cstring>
#include <ostream>
#include <string>
#include <vector>
@ -41,6 +42,7 @@ public:
void writeUInt56BE(uint64 value);
void writeInt64BE(int64 value);
void writeUInt64BE(uint64 value);
void writeVariableLengthUIntBE(uint64 value);
void writeFloat32BE(float32 value);
void writeFloat64BE(float64 value);
void writeInt16LE(int16 value);
@ -55,11 +57,13 @@ public:
void writeUInt56LE(uint64 value);
void writeInt64LE(int64 value);
void writeUInt64LE(uint64 value);
void writeVariableLengthUIntLE(uint64 value);
void writeFloat32LE(float32 value);
void writeFloat64LE(float64 value);
void writeString(const std::string &value);
void writeTerminatedString(const std::string &value);
void writeLengthPrefixedString(const std::string &value);
void writeLengthPrefixedCString(const char *value, std::size_t size);
void writeBool(bool value);
void writeSynchsafeUInt32BE(uint32 valueToConvertAndWrite);
void writeFixed8BE(float32 valueToConvertAndWrite);
@ -68,7 +72,24 @@ public:
void writeFixed8LE(float32 valueToConvertAndWrite);
void writeFixed16LE(float32 valueToConvertAndWrite);
// declare further overloads for write() to ease use of BinaryWriter in templates
void write(char oneChar);
void write(byte oneByte);
void write(bool oneBool);
void write(const std::string &lengthPrefixedString);
void write(const char *lengthPrefixedString);
void write(int16 one16BitInt);
void write(uint16 one16BitUint);
void write(int32 one32BitInt);
void write(uint32 one32BitUint);
void write(int64 one64BitInt);
void write(uint64 one64BitUint);
void write(float32 one32BitFloat);
void write(float64 one64BitFloat);
private:
void writeVariableLengthInteger(uint64 size, void (*getBytes)(uint64, char *));
std::ostream *m_stream;
bool m_ownership;
char m_buffer[8];
@ -306,6 +327,15 @@ inline void BinaryWriter::writeUInt64BE(uint64 value)
m_stream->write(m_buffer, sizeof(uint64));
}
/*!
* \brief Writes an up to 8 byte long big endian unsigned integer to the current stream and advances the current position of the stream by one to eight bytes.
* \throws Throws ConversionException if \a value exceeds the maximum.
*/
inline void BinaryWriter::writeVariableLengthUIntBE(uint64 value)
{
writeVariableLengthInteger(value, static_cast<void (*)(uint64, char *)>(&ConversionUtilities::BE::getBytes));
}
/*!
* \brief Writes a 32-bit big endian floating point \a value to the current stream and advances the current position of the stream by four bytes.
*/
@ -440,6 +470,15 @@ inline void BinaryWriter::writeUInt64LE(uint64 value)
m_stream->write(m_buffer, sizeof(uint64));
}
/*!
* \brief Writes an up to 8 byte long little endian unsigned integer to the current stream and advances the current position of the stream by one to eight bytes.
* \throws Throws ConversionException if \a value exceeds the maximum.
*/
inline void BinaryWriter::writeVariableLengthUIntLE(uint64 value)
{
writeVariableLengthInteger(value, static_cast<void (*)(uint64, char *)>(&ConversionUtilities::LE::getBytes));
}
/*!
* \brief Writes a 32-bit little endian floating point \a value to the current stream and advances the current position of the stream by four bytes.
*/
@ -525,6 +564,114 @@ inline void BinaryWriter::writeFixed16LE(float32 valueToConvertAndWrite)
{
writeUInt32LE(ConversionUtilities::toFixed16(valueToConvertAndWrite));
}
/*!
* \brief Writes a single character to the current stream and advances the current position of the stream by one byte.
*/
inline void BinaryWriter::write(char oneChar)
{
writeChar(oneChar);
}
/*!
* \brief Writes a single byte to the current stream and advances the current position of the stream by one byte.
*/
inline void BinaryWriter::write(byte oneByte)
{
writeByte(oneByte);
}
/*!
* \brief Writes a boolean value to the current stream and advances the current position of the stream by one byte.
*/
inline void BinaryWriter::write(bool oneBool)
{
writeBool(oneBool);
}
/*!
* \brief Writes the length of a string and the string itself to the current stream.
*
* Advances the current position of the stream by the length of the string plus the size of the length prefix.
*/
inline void BinaryWriter::write(const std::string &lengthPrefixedString)
{
writeLengthPrefixedCString(lengthPrefixedString.data(), lengthPrefixedString.size());
}
/*!
* \brief Writes the length of a string and the string itself to the current stream.
*
* Advances the current position of the stream by the length of the string plus the size of the length prefix.
*/
inline void BinaryWriter::write(const char *lengthPrefixedString)
{
writeLengthPrefixedCString(lengthPrefixedString, std::strlen(lengthPrefixedString));
}
/*!
* \brief Writes a 16-bit big endian signed integer to the current stream and advances the current position of the stream by two bytes.
*/
inline void BinaryWriter::write(int16 one16BitInt)
{
writeInt16BE(one16BitInt);
}
/*!
* \brief Writes a 16-bit big endian unsigned integer to the current stream and advances the current position of the stream by two bytes.
*/
inline void BinaryWriter::write(uint16 one16BitUint)
{
writeUInt16BE(one16BitUint);
}
/*!
* \brief Writes a 32-bit big endian signed integer to the current stream and advances the current position of the stream by four bytes.
*/
inline void BinaryWriter::write(int32 one32BitInt)
{
writeInt32BE(one32BitInt);
}
/*!
* \brief Writes a 32-bit big endian unsigned integer to the current stream and advances the current position of the stream by four bytes.
*/
inline void BinaryWriter::write(uint32 one32BitUint)
{
writeUInt32BE(one32BitUint);
}
/*!
* \brief Writes a 64-bit big endian signed integer to the current stream and advances the current position of the stream by eight bytes.
*/
inline void BinaryWriter::write(int64 one64BitInt)
{
writeInt64BE(one64BitInt);
}
/*!
* \brief Writes a 64-bit big endian unsigned integer to the current stream and advances the current position of the stream by eight bytes.
*/
inline void BinaryWriter::write(uint64 one64BitUint)
{
writeUInt64BE(one64BitUint);
}
/*!
* \brief Writes a 32-bit big endian floating point \a value to the current stream and advances the current position of the stream by four bytes.
*/
inline void BinaryWriter::write(float32 one32BitFloat)
{
writeFloat32BE(one32BitFloat);
}
/*!
* \brief Writes a 64-bit big endian floating point \a value to the current stream and advances the current position of the stream by eight bytes.
*/
inline void BinaryWriter::write(float64 one64BitFloat)
{
writeFloat64BE(one64BitFloat);
}
} // namespace IoUtilities
#endif // IO_UTILITIES_BINARYWRITER_H