C++ most optimal conversion - c++

Is that most optimal for server (speed) conversion of data ?
Could I change it for better performance ?
This is used in packet parser to set/get packet data.
void Packet::setChar(char val, unsigned int offset)
{
raw[offset + 8] = val;
}
short Packet::getChar(unsigned int offset)
{
return raw[offset + 8];
}
void Packet::setShort(short val, unsigned int offset)
{
raw[offset + 8] = val & 0xff;
raw[offset + 9] = (val >> 8) & 0xff;
}
short Packet::getShort(unsigned int offset)
{
return (short)((raw[offset + 9]&0xff) << 8) | (raw[offset + 8]&0xff);
}
void Packet::setInt(int val, unsigned int offset)
{
raw[offset + 8] = val & 0xff;
raw[offset + 9] = (val >> 8) & 0xff;
raw[offset + 10] = (val >> 16) & 0xff;
raw[offset + 11] = (val >> 24) & 0xff;
}
int Packet::getInt(unsigned int offset)
{
return (int)((raw[offset + 11]&0xff) << 24) | ((raw[offset + 10]&0xff) << 16) | ((raw[offset + 9]&0xff) << 8) | (raw[offset + 8]&0xff);
}
Class defs :
class Packet
{
public:
Packet(unsigned int length);
Packet(char * raw);
///header
void setChar(char val, unsigned int offset);
short getChar(unsigned int offset);
void setShort(short val, unsigned int offset);
short getShort(unsigned int offset);
void setInt(int val, unsigned int offset);
int getInt(unsigned int offset);
void setLong(long long val, unsigned int offset);
long getLong(unsigned int offset);
char * getRaw();
~Packet();
protected:
private:
char * raw;
};
#EDIT added class definitions
Char raw is inirialized with packet (new char).

I do agree with the comments saying "if it's not shown to be a problem, don't change it".
If your hardware is little endian, AND you either know that the offset is always aligned, or the processor supports unaligned accesses (e.g. x86), then you could speed up the setting of the larger data types by simply storing the whole item in one move (and yes, there will probably be people saying "it's undefined" - and it may well be undefined, but I've yet to see a compiler that doesn't do this correctly, because it's a fairly common thing to do in various types of code).
So something like this:
void Packet::setInt(int val, unsigned int offset)
{
int *ptr = static_cast<int*>(&raw[offset + 8]);
*ptr = val;
}
void Packet::getInt(int val, unsigned int offset)
{
int *ptr = static_cast<int*>(&raw[offset + 8]);
return *ptr;
}
Another thing I would DEFINITELY do is to ensure that the functions are present in a headerfile, so that the compiler has the choice to inline the functions. This will quite likely give you MORE benefit than fiddling with the code inside the functions, because the overhead of calling a function vs. being able to use the function inline will be quite noticeable. So that would be my first step - assuming you think it is a problem in the first place. For most things, stuffing the data into the buffer is not the "slow part" of sending a data packet - it is either the forming of the content, or the bytes passing down the wire to the other machine (which of the two depends on how fast your line is, and what calculations go into preparing the data in the first place).

It looks like that your implementation is already almost as efficient as possible. It simply isn't possible to optimize it further without a major overhaul of the application and even then, you'll save only few CPU cycles.
By the way, make sure that the function definitions are present in the header file, or #included to it. Otherwise, each output operation will need a function call, which is quite expensive for what you're doing.

Related

Access bits in memory

I want to assemble a message bit by bit, then handle the message as a vector of unsigned characters ( e.g. to calculate the CRC )
I can assemble the message OK, using either a std::vector<bool> or a std::bitset
I can copy the assembled message to a std::vector doing it bit by bit. ( Note: the meesage is padded so that its length is an integer number of bytes )
// assemble message
std::vector<bool> bitMessage;
...
// copy the bits one by one into bytes and add them to the message
std::vector<unsigned char> myMessage;
// loop over bytes
for (int kbyte = 0;
kbyte < bitMessage.size() / 8;
kbyte++)
{
unsigned char byte = 0;
// loop over bits
for (int kbit = 0;
kbit < 8;
kbit++)
{
// add bit to byte
byte += bitMessage[8 * kbyte + kbit] << kbit;
}
// add byte to message
myMessage.push_back(byte);
}
This works.
But it seems awfully slow! I would like to use std::memcpy.
For a 'normal' vector I would do
memcpy(
myMessage.data(),
bitMessage.data(),
bitMessage.size() / 8 );
or
memcpy(
&myMessage[0],
&bitMessage[0],
bitMessage.size() / 8 );
but neither of these methods is possible with either a vector<bool> or bitset
Question: Is there a way to get a pointer to the memory where the bits are stored?
The answer is: not with std::vector<bool> or std::bitset
However, with some hints , especially from #Ayxan Haqverdili, it is possible to write a small class that will accept single bits and construct a well mannered std::vector<unsigned char> as we go along.
/** Build a message bit by bit, creating an unsigned character vector of integer length
*
* Hides the messy bit twiddling required,
* allowing bits to be added to the end of the message
*
* The message is automatically padded at the end with zeroes
*/
class cTwiddle
{
public:
std::vector<unsigned char> myMessage;
cTwiddle() : myBitLength(0) {}
/** add a bit to end of message
* #param[in] bit
*/
void add(bool bit)
{
// check if message vector is full
if (!(myBitLength % 8))
{
// add byte to end of message
myMessage.push_back(0);
}
// control order bits are added to a byte
int shift = 7 - (myBitLength % 8); // add bits from left to right ( MSB first )
// int shift = (myBitLength % 8); // add bits from right to left ( LSB first )
myMessage.back() += (1 & bit) << shift;
myBitLength++;
}
private:
int myBitLength;
};
Apparently neither of those classes define the layout. Just write your own class and define the layout you want:
template <int size>
class BitSet final {
private:
unsigned char buffer[size / 8 + (size % 8 != 0)] = {};
public:
constexpr bool get(size_t index) const noexcept {
return (buffer[index / 8] >> (index % 8)) & 1U;
}
constexpr void set(size_t index) noexcept {
buffer[index / 8] |= (1U << (index % 8));
}
constexpr void clear(size_t index) noexcept {
buffer[index / 8] &= ~(1U << (index % 8));
}
};
Memcpy-ing this class is perfectly fine. Otherwise, you might also provide direct access to the byte array.
Alternatively, you can dynamically allocate the buffer:
#include <memory>
class DynBitSet final {
private:
size_t size = 0;
std::unique_ptr<unsigned char[]> buffer;
public:
explicit DynBitSet(size_t bitsize)
: size(bitsize / 8 + (bitsize % 8 != 0)),
buffer(new unsigned char[size]{}) {}
bool get(size_t index) const noexcept {
return (buffer[index / 8] >> (index % 8)) & 1U;
}
void set(size_t index) noexcept { buffer[index / 8] |= (1U << (index % 8)); }
void clear(size_t index) noexcept {
buffer[index / 8] &= ~(1U << (index % 8));
}
auto bitSize() const noexcept { return size * 8; }
auto byteSize() const noexcept { return size; }
auto const* byteBuffer() const noexcept { return buffer.get(); }
};
Is there a way to get a pointer to the memory where the bits are stored [in std::vector]?
No. The idea is that it should be not possible.
Is there a way
Fun fact: In glibc the member in iterator is public.
#include <vector>
#include <iostream>
int main() {
std::vector<bool> vec{1,0,1,0,1,1,1,1};
std::cout << *vec.begin()._M_p << '\n';
}

How do I extract little-endian unsigned short from long pointer?

I have a long pointer value that points to a 20 byte header structure followed by a larger array. Dec(57987104)=Hex(0374D020). All the values are stored little endian. 1400 when swapped is 0014 which in decimal is 20.
The question here is how do I get the first value which is a 2 byte unsigned short. I have a C++ dll to convert this for me. I'm running Windows 10.
GetCellData_API unsigned short __stdcall getUnsignedShort(unsigned long ptr)
{
unsigned long *p = &ptr;
unsigned short ret = *p;
return ret;
}
But when I call this from VBA using Debug.Print getUnsignedShort(57987104) I get 30008 when it should be 20.
I might need to do an endian swap but I'm not sure how to incorporate this from CodeGuru: How do I convert between big-endian and little-endian values?
inline void endian_swap(unsigned short& x)
{
x = (x >> 8) |
(x << 8);
}
How do I extract little endian unsigned short from long pointer?
I think I'd be inclined to write your interface function in terms of a general template function that describes the operation:
#include <utility>
#include <cstdint>
// Code for the general case
// you'll be amazed at the compiler's optimiser
template<class Integral>
auto extract_be(const std::uint8_t* buffer)
{
using accumulator_type = std::make_unsigned_t<Integral>;
auto acc = accumulator_type(0);
auto count = sizeof(Integral);
while(count--)
{
acc |= accumulator_type(*buffer++) << (8 * count);
}
return Integral(acc);
}
GetCellData_API unsigned short __stdcall getUnsignedShort(std::uintptr_t ptr)
{
return extract_be<std::uint16_t>(reinterpret_cast<const std::uint8_t*>(ptr));
}
As you can see from the demo on godbolt, the compiler does all the hard work for you.
Note that since we know the size of the data, I have used the sized integer types exported from <cstdint> in case this code needs to be ported to another platform.
EDIT:
Just realised that your data is actually LITTLE ENDIAN :)
template<class Integral>
auto extract_le(const std::uint8_t* buffer)
{
using accumulator_type = std::make_unsigned_t<Integral>;
auto acc = accumulator_type(0);
constexpr auto size = sizeof(Integral);
for(std::size_t count = 0 ; count < size ; ++count)
{
acc |= accumulator_type(*buffer++) << (8 * count);
}
return Integral(acc);
}
GetCellData_API unsigned short __stdcall getUnsignedShort(std::uintptr_t ptr)
{
return extract_le<std::uint16_t>(reinterpret_cast<const std::uint8_t*>(ptr));
}
Lets say youre pointing with pulong pulong[6] you are pointing 6 sixth member of the table
unsigned short psh*;
unsigned char puchar*
unsigend char ptable[4];
ZeroMemory(ptable,4);
puchar[3]=((char *)( &pulong[6]))[0];
puchar[2]=((char *)( &pulong[6]))[1];
puchar[1]=((char *)( &pulong[6]))[2];
puchar[0]=((char *)( &pulong[6]))[3];
psh=(unsigned short *) puchar;
//first one
psh[0];
//second one
psh[1];
THis was what was in my mind while mistaking me

How to byteswap a double?

I'm trying to write a byteswap routine for a C++ program running on Win XP. I'm compiling with Visual Studio 2008. This is what I've come up with:
int byteswap(int v) // This is good
{
return _byteswap_ulong(v);
}
double byteswap(double v) // This doesn't work for some values
{
union { // This trick is first used in Quake2 source I believe :D
__int64 i;
double d;
} conv;
conv.d = v;
conv.i = _byteswap_uint64(conv.i);
return conv.d;
}
And a function to test:
void testit() {
double a, b, c;
CString str;
for (a = -100; a < 100; a += 0.01) {
b = byteswap(a);
c = byteswap(b);
if (a != c) {
str.Format("%15.15f %15.15f %15.15f", a, c, a - c);
}
}
}
Getting these numbers not matching:
-76.789999999988126 -76.790000000017230 0.000000000029104
-30.499999999987718 -30.499999999994994 0.000000000007276
41.790000000014508 41.790000000029060 -0.000000000014552
90.330000000023560 90.330000000052664 -0.000000000029104
This is after having read through:
How do I convert between big-endian and little-endian values in C++?
Little Endian - Big Endian Problem
You can't use << and >> on double, by the way (unless I'm mistaken?)
Although a double in main memory is 64 bits, on x86 CPUs double-precision registers are 80 bits wide. So if one of your values is stored in a register throughout, but the other makes a round-trip through main memory and is truncated to 64 bits, this could explain the small differences you're seeing.
Maybe you can force variables to live in main memory by taking their address (and printing it, to prevent the compiler from optimizing it out), but I'm not certain that this is guaranteed to work.
b = byteswap(a);
That's a problem. After swapping the bytes, the value is no longer a proper double. Storing it back to a double is going to cause subtle problems when the FPU normalizes the value. You have to store it back into an __int64 (long long). Modify the return type of the method.
Try 3
Okay, found out there's a better way. The other way you have to worry about the order you pack/unpack stuff. This way you don't:
// int and float
static void swap4(void *v)
{
char in[4], out[4];
memcpy(in, v, 4);
out[0] = in[3];
out[1] = in[2];
out[2] = in[1];
out[3] = in[0];
memcpy(v, out, 4);
}
// double
static void swap8(void *v)
{
char in[8], out[8];
memcpy(in, v, 8);
out[0] = in[7];
out[1] = in[6];
out[2] = in[5];
out[3] = in[4];
out[4] = in[3];
out[5] = in[2];
out[6] = in[1];
out[7] = in[0];
memcpy(v, out, 8);
}
typedef struct
{
int theint;
float thefloat;
double thedouble;
} mystruct;
static void swap_mystruct(void *buf)
{
mystruct *ps = (mystruct *) buf;
swap4(&ps->theint);
swap4(&ps->thefloat);
swap8(&ps->thedouble);
}
Send:
char buf[sizeof (mystruct)];
memcpy(buf, &s, sizeof (mystruct));
swap_mystruct(buf);
Recv:
mystruct s;
swap_mystruct(buf);
memcpy(&s, buf, sizeof (mystruct));
Try 2
Okay, got it working! Hans Passant was right. They got me thinking with the "no longer a proper double" comment. So you can't byteswap a float into another float because then it might be in an improper format, so you have to byteswap to a char array and unswap back. This is the code I used:
int pack(int value, char *buf)
{
union temp {
int value;
char c[4];
} in, out;
in.value = value;
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(buf, out.c, 4);
return 4;
}
int pack(float value, char *buf)
{
union temp {
float value;
char c[4];
} in, out;
in.value = value;
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(buf, out.c, 4);
return 4;
}
int pack(double value, char *buf)
{
union temp {
double value;
char c[8];
} in, out;
in.value = value;
out.c[0] = in.c[7];
out.c[1] = in.c[6];
out.c[2] = in.c[5];
out.c[3] = in.c[4];
out.c[4] = in.c[3];
out.c[5] = in.c[2];
out.c[6] = in.c[1];
out.c[7] = in.c[0];
memcpy(buf, out.c, 8);
return 8;
}
int unpack(char *buf, int *value)
{
union temp {
int value;
char c[4];
} in, out;
memcpy(in.c, buf, 4);
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(value, &out.value, 4);
return 4;
}
int unpack(char *buf, float *value)
{
union temp {
float value;
char c[4];
} in, out;
memcpy(in.c, buf, 4);
out.c[0] = in.c[3];
out.c[1] = in.c[2];
out.c[2] = in.c[1];
out.c[3] = in.c[0];
memcpy(value, &out.value, 4);
return 4;
}
int unpack(char *buf, double *value)
{
union temp {
double value;
char c[8];
} in, out;
memcpy(in.c, buf, 8);
out.c[0] = in.c[7];
out.c[1] = in.c[6];
out.c[2] = in.c[5];
out.c[3] = in.c[4];
out.c[4] = in.c[3];
out.c[5] = in.c[2];
out.c[6] = in.c[1];
out.c[7] = in.c[0];
memcpy(value, &out.value, 8);
return 8;
}
And a simple test function:
typedef struct
{
int theint;
float thefloat;
double thedouble;
} mystruct;
void PackStruct()
{
char buf[sizeof (mystruct)];
char *p;
p = buf;
mystruct foo, foo2;
foo.theint = 1;
foo.thefloat = 3.14f;
foo.thedouble = 400.5;
p += pack(foo.theint, p);
p += pack(foo.thefloat, p);
p += pack(foo.thedouble, p);
// Send or recv char array
p = buf;
p += unpack(p, &foo2.theint);
p += unpack(p, &foo2.thefloat);
p += unpack(p, &foo2.thedouble);
}
How to swap the bytes in any basic data type or array of bytes
ie: How to swap the bytes in place in any array, variable, or any other memory block, such as an int16_t, uint16_t, uint32_t, float, double, etc.:
Here's a way to improve the efficiency from 3 entire copy operations of the array to 1.5 entire copy operations of the array. See also the comments I left under your answer. I said:
Get rid of this: memcpy(in, v, 4); and just copy-swap straight into out from v, then memcpy the swapped values back from out into v. This saves you an entire unnecessary copy, reducing your copies of the entire array from 3 to 2.
There's also a further optimization to reduce the copies of the entire array from 2 to 1.5: copy the left half of the array into temporary variables, and the right-half of the array straight into the left-half, swapping as appropriately. Then copy from the temporary variables, which contain the old left-half of the array, into the right-half of the array, swapping as appropriately. This results in the equivalent of only 1.5 copy operations of the entire array, to be more efficient. Do all this in-place in the original array, aside from the temp variables you require for half of the array.
1. Here is my general C and C++ solution:
/// \brief Swap all the bytes in an array to convert from little-endian
/// byte order to big-endian byte order, or vice versa.
/// \note Works for arrays of any size. Swaps the bytes **in place**
/// in the array.
/// \param[in,out] byte_array The array in which to swap the bytes in-place.
/// \param[in] len The length (in bytes) of the array.
/// \return None
void swap_bytes_in_array(uint8_t * byte_array, size_t len)
{
size_t i_left = 0; // index for left side of the array
size_t i_right = len - 1; // index for right side of the array
while (i_left < i_right)
{
// swap left and right bytes
uint8_t left_copy = byte_array[i_left];
byte_array[i_left] = byte_array[i_right];
byte_array[i_right] = left_copy;
i_left++;
i_right--;
}
}
Usage:
// array of bytes
uint8_t bytes_array[16];
// Swap the bytes in this array of bytes in place
swap_bytes_in_array(bytes_array, sizeof(bytes_array));
double d;
// Swap the bytes in the double in place
swap_bytes_in_array((uint8_t*)(&d), sizeof(d));
uint64_t u64;
// swap the bytes in a uint64_t in place
swap_bytes_in_array((uint8_t*)(&u64), sizeof(u64));
2. And here is an optional C++ template wrapper around that to make it even easier to use in C++:
template <typename T>
void swap_bytes(T *var)
{
// Note that `sizeof(*var)` is the exact same thing as `sizeof(T)`
swap_bytes_in_array((uint8_t*)var, sizeof(*var));
}
Usage:
double d;
// Swap the bytes in the double in place
swap_bytes(&d);
uint64_t u64;
// swap the bytes in a uint64_t in place
swap_bytes(&u64);
Notes & unanswered questions
Note, however, that #Hans Passant seems to be onto something here. Although the above works perfectly on any signed or unsigned integer type, and seems to work on float and double for me too, it seems to be broken on long double. I think it's because when I store the swapped long double back into a long double variable, if it is determined to be not-a-valid long double representation anymore, something automatically changes a few of the swapped bytes or something. I'm not entirely sure.
On many 64-bit systems, long double is 16 bytes, so perhaps the solution is to keep the swapped version of the long double inside a 16-byte array and NOT attempt to use it or cast it back to a long double from the uint8_t 16-byte array until either A) it has been sent to the receiver (where the endianness of the system is opposite, so it's in good shape now) and/or B) byte-swapped back again so it's a valid long double again.
Keep the above in mind in case you see problems with float or double types too, as I see with only long double types.
Linux byteswap and endianness and host-to-network byte order utilities
Linux also has a bunch of built-in utilities via gcc GNU extensions that you can use. See:
https://man7.org/linux/man-pages/man3/bswap.3.html - #include <byteswap.h>
https://man7.org/linux/man-pages/man3/endian.3.html - #include <endian.h>
https://man7.org/linux/man-pages/man3/byteorder.3.html - #include <arpa/inet.h> - generally used for network sockets (Ethernet packets) and things; inet stands for "internet"

Converting float values from big endian to little endian

Is it possible to convert floats from big to little endian? I have a big endian value from a PowerPC platform that I am sendING via TCP to a Windows process (little endian). This value is a float, but when I memcpy the value into a Win32 float type and then call _byteswap_ulongon that value, I always get 0.0000?
What am I doing wrong?
simply reverse the four bytes works
float ReverseFloat( const float inFloat )
{
float retVal;
char *floatToConvert = ( char* ) & inFloat;
char *returnFloat = ( char* ) & retVal;
// swap the bytes into a temporary buffer
returnFloat[0] = floatToConvert[3];
returnFloat[1] = floatToConvert[2];
returnFloat[2] = floatToConvert[1];
returnFloat[3] = floatToConvert[0];
return retVal;
}
Here is a function can reverse byte order of any type.
template <typename T>
T bswap(T val) {
T retVal;
char *pVal = (char*) &val;
char *pRetVal = (char*)&retVal;
int size = sizeof(T);
for(int i=0; i<size; i++) {
pRetVal[size-1-i] = pVal[i];
}
return retVal;
}
I found something roughly like this a long time ago. It was good for a laugh, but ingest at your own peril. I've not even compiled it:
void * endian_swap(void * arg)
{
unsigned int n = *((int*)arg);
n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00);
n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
*arg = n;
return arg;
}
An elegant way to do the byte exchange is to use a union:
float big2little (float f)
{
union
{
float f;
char b[4];
} src, dst;
src.f = f;
dst.b[3] = src.b[0];
dst.b[2] = src.b[1];
dst.b[1] = src.b[2];
dst.b[0] = src.b[3];
return dst.f;
}
Following jjmerelo's recommendation to write a loop, a more generic solution could be:
typedef float number_t;
#define NUMBER_SIZE sizeof(number_t)
number_t big2little (number_t n)
{
union
{
number_t n;
char b[NUMBER_SIZE];
} src, dst;
src.n = n;
for (size_t i=0; i<NUMBER_SIZE; i++)
dst.b[i] = src.b[NUMBER_SIZE-1 - i];
return dst.n;
}
Don't memcpy the data directly into a float type. Keep it as char data, swap the bytes and then treat it as a float.
It might be easier to use the ntoa and related functions to convert from network to host and from host to network..the advantage it would be portable. Here is a link to an article that explains how to do this.
From SDL_endian.h with slight changes:
std::uint32_t Swap32(std::uint32_t x)
{
return static_cast<std::uint32_t>((x << 24) | ((x << 8) & 0x00FF0000) |
((x >> 8) & 0x0000FF00) | (x >> 24));
}
float SwapFloat(float x)
{
union
{
float f;
std::uint32_t ui32;
} swapper;
swapper.f = x;
swapper.ui32 = Swap32(swapper.ui32);
return swapper.f;
}
This value is a float, but when I "memcpy" the value into a win32 float type and then call _byteswap_ulong on that value, I always get 0.0000?
This should work. Can you post the code you have?
However, if you care for performance (perhaps you do not, in that case you can ignore the rest), it should be possible to avoid memcpy, either by directly loading it into the target location and swapping the bytes there, or using a swap which does the swapping while copying.
in some case, especially on modbus: network byte order for a float is:
nfloat[0] = float[1]
nfloat[1] = float[0]
nfloat[2] = float[3]
nfloat[3] = float[2]
Boost libraries have already been mentioned by #morteza and #AnotherParker, stating that the support for float was removed. However, it was added back in a subset of the library since they wrote their comments.
Using Boost.Endian conversion functions, version 1.77.0 as I wrote this answer, you can do the following:
float input = /* some value */;
float reversed = input;
boost::endian::endian_reverse_inplace(reversed);
Check the FAQ to learn why the support was removed then partially added back (mainly, because a reversed float may not be valid anymore) and here for the support history.

Data structures with different sized bit fields

If I have a requirement to create a data structure that has the following fields:
16-bit Size field
3-bit Version field
1-bit CRC field
How would I code this struct? I know the Size field would be an unsigned short type, but what about the other two fields?
First, unsigned short isn't guaranteed to be only 16 bits, just at least 16 bits.
You could do this:
struct Data
{
unsigned short size : 16;
unsigned char version : 3;
unsigned char crc : 1;
};
Assuming you want no padding between the fields, you'll have to issue the appropriate instructions to your compiler. With gcc, you can decorate the structure with __attribute__((packed)):
struct Data
{
// ...
} __attribute__((packed));
In Visual C++, you can use #pragma pack:
#pragma pack(push, 0)
struct Data
{
// ...
};
#pragma pack(pop)
The following class implements the fields you are looking for as a kind of bitfields.
struct Identifier
{
unsigned int a; // only bits 0-19 are used
unsigned int getSize() const {
return a & 0xFFFF; // access bits 0-15
}
unsigned int getVersion() const {
return (a >> 16) & 7; // access bits 16-18
}
unsigned int getCrc() const {
return (a >> 19) & 1; // access bit 19
}
void setSize(unsigned int size) {
a = a - (a & 0xFFF) + (size & 0xFFF);
}
void setVersion(unsigned int version) {
a = a - (a & (7<<16)) + ((version & 7) << 16);
}
void setCrc(unsigned int crc) {
a = a - (a & (1<<19)) + ((crc & 1) << 19);
}
};