I'm trying to follow an article written at https://accu.org/index.php/journals/2317 I found it kind of interesting now that I'm trying to dive in serializaton / file saving / loading for some administrative tools I'm working on. I tagged it c++ because Ill wrap it on a class after I make it work (hopefully with your help).
Now, this is for learning purposes, as I don't intend to use boost or any other serialization library at the moment. I want to know or try to find out, out these things work.
Currently, I had to do some slights modifications in order to work, because assertions where always false and had a problem trying to write outside allocated memory with memcpy.
Right now, it compiles, but when I Serialize and Deserialize the structs, the data is not the same.
Please review and help me out, or point me in the right direction. After the code, I'll try to explain how I understand it.
#include <iostream>
#include <cassert>
struct Y
{
int yy;
};
struct X
{
int xx;
struct Y* y = nullptr;
int z;
};
// Changed OutMemStram and InMemStream for IOMemStream, same data
struct IOMemStream
{
// changed from uint8_t* to char*
char* pp;
char* ppEnd;
};
// Output
inline void WriteToStream( IOMemStream* dst, void* p, size_t sz )
{
dst->pp = (char*)p; // original code doesn't contain this line
dst->ppEnd = (char*)p + sz; // original code doesn't contain this line
assert( (dst->pp + sz) <= dst->ppEnd );
memcpy( dst->pp, p, sz );
dst->pp += sz;
}
void SerializeX( IOMemStream* dst, X* x )
{
WriteToStream( dst, x, sizeof( X ) );
WriteToStream( dst, x->y, sizeof( Y ) );
}
// Input
inline void ReadFromStream( IOMemStream* src, void* p, size_t sz )
{
//assert( (src->pp + sz) <= src->ppEnd );
memcpy( p, src->pp, sz );
src->pp += sz;
}
void DeserializeX( IOMemStream* src, X* x )
{
ReadFromStream( src, x, sizeof( X ) );
// x->y contains garbage at this point(!)
// ok, not exactly garbage - but a pointer
// which is utterly invalid in our current space
x->y = new Y;
assert( x->y );
ReadFromStream( src, x->y, sizeof( Y ) );
}
// Usage sample
int main()
{
// Assume struct x was previously filled by other function
X x;
x.xx = 1000;
x.z = 2000;
x.y = new Y;
x.y->yy = 3000;
// IO buffer
IOMemStream ioms;
// Test for output
SerializeX( &ioms, &x );
// Test for input
X x1;
DeserializeX( &ioms, &x1 );
// x1.xx should be 1000 and x1.< should be 2000
std::cout << x1.xx << ", " << x1.z << std::endl;
delete x.y;
delete x1.y;
//delete ioms.pp; // gets exception
std::cin.get();
return 0;
}
This is how I understand (or didn't understand).
- Struct X, contains 2 ints and 1 struct pointer to a Y struct, which, assuming int size = 4, then X's size will be 12 bytes. Y it's 4 bytes.
- IOMemStream contains pointers for both struct X and struct Y.
- IOMemStream->pp and IOMemStream->ppEnd should have 12 bytes and 4 bytes from the struct X and X->Y.
- Function WriteToStream packs the bytes from the structs to the pp and ppEnd pointers, after X is assigned, the pointer is incremented by the size to get ready for Y struct. (in the original article, assertion is made without assigning variables pp and ppEnd from
- IOMemStream) Function SerializeX uses WriteToStrea for both X and Y structs.
Almost the same for the Deserialization but in inverse order and ReadFromStream will allocate memory for struct Y.
I got lost from here, as the values are not the same from serialization and deserialization. Also, I hope I understood it properly :D
Thank you in advance!