I am given a C++ framework (that I can't change) for IO operations that works like this:
block_size
which is the size of block that is read or writtenstruct Operation { size_t block; // index of external memory block void * data; // here the data will be written or read from }
The the actual read or write looks like this:
Operation o; o.block = 42; // index of block i want to read from o.data = // where i want the data to end up io.read(o);
The problem is that the read/write is implemented with memcpy
and it always moves block of block_size
bytes. However when I want to save and load my class X
to this external memory I have a problem, because probably sizeof(X) != block_size
.
So when I would do this:
X x; Operation o; o.block = 42; // index of block i want to read from o.data = &x; io.read(o); io.write(o);
If sizeof(X) < block_size
I have a problem during the read, because more bytes than I want will be read, possibly corrupting the stack.
If sizeof(X) > block_size
I have a problem during write, because not every byte of X
will be written so I have an incomplete backup of it.
I want to use each block for one instance of X
and I can make sure that sizeof(X) < block_size
. Is there some way how to add some padding bytes to X
, so it has exactly block_size
bytes?
X x;
Operation o;
o.block = 42; // index of block i want to read from
o.data = &x;
io.read(o);
Well, you can't do that, as you yourself said. You must point to a block_size
block, then copy into the relevant class:
BYTE buffer[block_size];
X x;
Operation o;
o.block = 42; // index of block i want to write to
o.data = buffer;
io.read(o);
memcpy(&x, buffer, sizeof(X));
Ditto for writes (you can't simply write whatever happens to be after the end of X because you may fall off the cliff into an unmapped page):
BYTE buffer[block_size];
X x;
Operation o;
o.block = 42; // index of block i want to read from
o.data = buffer;
memcpy(buffer, &x, sizeof(X));
io.write(o);
I won't comment on the sanity of the interface itself. And static_assert(sizeof(X) <= block_size)
. And the type X
must be memcopy safe.
There are more tricks in the book, like make the sizeof(X)
always match block_size
(via padding) or use allocation tricks (always allocate X in block_size regions, never use on stack). But copy before write/after read is the simplest, least tricky.