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:
parent
b77607f3e0
commit
5deb077fe5
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue