Binary Deserialization
Serialization based on binary streams of data can become quite sophisticated, but I decided to go for a relatively easy approach which implies that the size of the data is known a priori. For deserialization, this is pretty simple – for example, just take the size of the file -, so we’ll start with that.
#ifndef VTE_CORE_BINARYDESERIALIZER_H #define VTE_CORE_BINARYDESERIALIZER_H #include <core/Types.h> #include "Deserializer.h" namespace vte { namespace core { class BinaryDeserializer : public Deserializer { public: BinaryDeserializer( u8* buffer, u32 bufferSize ); ~BinaryDeserializer(); void beginAttribute( const std::string& name ) { } u32 readNumberOfElements(); void readParameter( const std::string& name, std::string& value ); void readParameter( const std::string& name, s32& value, std::string& readable ); void readParameter( const std::string& name, bool& value ); void readParameter( const std::string& name, u32& value ); void readParameter( const std::string& name, f32& value ); void readParameter( const std::string& name, vec3& value ); void readParameter( const std::string& name, irr::video::SColorf& value ); void endAttribute() { } private: u8* buffer; u32 bufferSize; u32 pointer; bool overrun; }; } // namespace core } // namespace vte #endif
As you can see, beginAttribute
and endAttribute
have empty implementations since they are not required for binary deserialization.
The constructor takes a pointer to the data buffer and the buffer size which are saved in the buffer
and bufferSize
attributes. Moreover, it keeps track of its current reading position in pointer
and whether a buffer overrun has occurred in overrun
.
vte::core::BinaryDeserializer::BinaryDeserializer( u8* buffer, u32 bufferSize ) : buffer( buffer ), bufferSize( bufferSize ), pointer( 0 ), overrun( false ) { } vte::core::BinaryDeserializer::~BinaryDeserializer() { }
readNumberOfElements
first checks whether an overrun has already occurred or will occur if it attempts to read the next four bytes from the buffer. If possible, it reads four bytes into a variable of type u32
(which is a signed int
) and pushes the pointer
four bytes further.
This principle repeats for all data types which are read using the readParameter
methods. The version for string
attributes first reads the length of the string
and then the string
data into a temporary buffer to eventually create a string
object; the version for bool
reads a single byte which is expected to has a value of either 0 for “false” or anything else for “true”.
u32 vte::core::BinaryDeserializer::readNumberOfElements() { VTE_ASSERT( !overrun ); if( overrun || pointer + 4 > bufferSize ) { overrun = true; return 0; } u32 value; memcpy( &value, (void*)( buffer + pointer ), 4 ); pointer += 4; return value; } void vte::core::BinaryDeserializer::readParameter( const std::string& name, std::string& value ) { VTE_ASSERT( !overrun ); u32 len = readNumberOfElements(); if( overrun || pointer + len > bufferSize ) { overrun = true; return; } if( len > 0 ) { value.resize( len ); memcpy( (void*) value.data(), (void*)( buffer + pointer ), len ); } pointer += len; } void vte::core::BinaryDeserializer::readParameter( const std::string& name, bool& value ) { VTE_ASSERT( !overrun ); if( overrun || pointer + 1 > bufferSize ) { overrun = true; return; } value = buffer[ pointer ] == 0 ? false : true; pointer++; }
4 Comments