Pack bits in a struct in C++ / Arduino - c++

I have a struct:
typedef struct {
uint8_t month; // 1..12 [4 bits]
uint8_t date; // 1..31 [5 bits]
uint8_t hour; // 00..23 [5 bits]
uint8_t minute; // 00..59 [6 bits]
uint8_t second; // 00..59 [6 bits]
} TimeStamp;
but I would like to pack it so it only consumes four bytes instead of five.
Is there a way of shifting the bits to create a tighter struct?
It might not seem much, but it is going into EEPROM, so one byte saved is an extra 512 bytes in a 4 KB page (and I can use those extra six bits left over for something else too).

You're looking for bitfields.
They look like this:
typedef struct {
uint32_t month : 4; // 1..12 [4 bits]
uint32_t date : 5; // 1..31 [5 bits]
uint32_t hour : 5; // 00..23 [5 bits]
uint32_t minute : 6; // 00..59 [6 bits]
uint32_t second : 6; // 00..59 [6 bits]
} TimeStamp;
Depending on your compiler, in order to fit into four bytes with no padding, the size of the members must be four bytes (i.e. uint32_t) in this case. Otherwise, the struct members will get padded to not overflow on each byte boundary, resulting in a struct of five bytes, if using uint8_t. Using this as a general rule should help prevent compiler discrepancies.
Here's an MSDN link that goes a bit in depth into bitfields:
C++ Bit Fields

Bitfields are one "right" way to do this in general, but why not just store seconds since the start of the year instead? 4 bytes is enough to comfortably store these; in fact, 4 bytes are enough to store the seconds between 1970 and 2038. Getting the other information out of it is then a simple exercise as long as you know the current year (which you could store together with the rest of the information as long as the range of times you're interested in covers less than 70 years (and even then you could just group timestamps into 68 year ranges and store an offset for each range).

Another solution is to store the values in one 32 bits variable and retrieve the individual items with bitshifting.
uint32_t timestamp = xxxx;
uint8_t month = timestamp & 0x0F;
uint8_t date = (timestamp & 0x1F0) >> 4;
uint8_t hour = (timestamp & 0x3E00) >> 9;
uint8_t minute = (timestamp & 0xFC000) >> 14;
uint8_t second = (timestamp & 0x3F00000) >> 20;

If you can deal with two-second accuracy, the MS-DOS timestamp format used 16 bits to hold the date (year-1980 as 7 bits, month as 4, day as 5) and 16 bits for the time (hour as five, minute as six, seconds as five). On a processor like the Arduino, it may be possible to write code that splits values across a 16-bit boundary, but I think code will be more efficient if you can avoid such a split (as MS-DOS did by accepting two-second accuracy).
Otherwise, as was noted in another answer, using a 32-bit number of seconds since some base time will often be more efficient than trying to keep track of things in "calendar format". If all you ever need to do is advance from one calendar-format date to the next, the code to do that may be simpler than code to convert between calendar dates and linear dates, but if you need to do much of anything else (even step backward from a date to the previous one) you'll likely be better off converting dates to/from linear format when they're input or displayed, and otherwise simply work with linear numbers of seconds.
Working with linear numbers of seconds can be made more convenient if you pick as a baseline date March 1 of a leap year. Then while the date exceeds 1461, subtract that from the date and add 4 to the year (16-bit comparison and subtraction are efficient on the Arduino, and even in 2040 the loop may still take less time than a single 16x16 division). If the date exceeds 364, subtract 365 and increment the year, and try that up to twice more [if the date is 365 after the third subtraction, leave it].
Some care is needed to ensure that all corner cases work correctly, but even on a little 8-bit or 16-bit micro, conversions can be surprisingly efficient.

Related

Can i store 2 * 12bit unsigned numbers in a file as 3 bytes in C++ using bit fields?

I`m working on an LZW compression app in C++. Since there are no data types that can store 12 bit numbers for representing table elements up to 4095 I thought that I can store 2 of those nrs as 3 bytes in a file and then read them as a struct with 2 unsigned short members. Is there a way to do that or I should just use unsigned short? This is what I have tried but it stores 4 bytes as there are 2 unsigned short members.
#define BITS 12
struct LZWStruct {
unsigned short code1 : BITS;
unsigned short code2 : BITS;
};
int main() {
LZWStruct test;
test.code1 = 144;
test.code2 = 233;
FILE* f = fopen("binary.bin", "wb");
fwrite(&test, sizeof(test), 1, f);
fclose(f);
}
Your question title and question body are two different questions with different answers.
No, you absolutely cannot store 3 * 12-bit unsigned numbers (36 bits) in four bytes (32 bits).
Yes, you can store two 12-bit numbers (24 bits) in three bytes (24 bits).
The bit fields in C++, inherited from C, that you are trying to use do not guarantee exactly how the bits are packed in the structure, so you cannot know which three bytes in the structure have your data. You should simply use the shift and or operators to put them in an integer. Then you will know exactly which three bytes to write to the file.
Then to be portable, in particular not dependent on the endianess of the machine, you should write bytes from the integer also using the shift operator. If you write using a pointer to the integer, it won't be portable.
In your example, you could have tried fwrite(&test, 3, 1, f), and it might work, if the compiler put the codes in the low bits of test, and if your machine is little-endian. Otherwise, no.
So to do it reliably:
Put in an integer:
unsigned short code1;
unsigned short code2;
uint32_t test = (code1 & 0x3ff) | ((uint32_t)(code2 & 0x3ff) << 12);
Write to a file:
putc(test, f);
putc(test >> 8, f);
putc(test >> 16, f);
You can skip the intermediate step if you like:
putc(code1, f);
putc(((code1 >> 8) & 0xf) | (code2 << 4), f);
putc(code2 >> 4, f);
(In the above I am assuring that I only store the low 12 bits of each code with the & operators, in case the bits above the low 12 are not zero. If you know for certain that the code values are less than 4096, then you can remove the & operations above.)
From here, multiple adjacent bit fields are usually packed together. The special unnamed bit field of size zero can be forced to break up padding. It specifies that the next bit field begins at the beginning of its allocation unit. Use sizeof to verify the size of your structure.
The exact packing, however, may depend on platform and compiler. This may be less a problem if the data are later loaded by the same program, or some closely related, but may be an issue for some generic format.

C++ bitset with if statement

I was wondering if there is anyway to use specific range of bits with if statement.
im using a fpga to send 8bits binary data over usb to pc. each transaction has 3x 8 bits packets. in each packet first four bits are generated by outside module and i want to send control data in the last four bits.
=>
usb interface accepts data as integers and i've got bitset function to convert integers to 8bit binary. i want to use last four bits to use with if statements. is there any way i can do this?
thanks in advance
If you have an unsigned char as input:
void foo(uint8_t x) {
uint8_t top4 = x >> 4; // moves top 4 bits down by 4 positions
uint8_t bottom4 = x & 0x0f; // zeros out top 4 bits, leaving bottom 4
}
This will work with int, short, etc., types as well, but you would need to &-mask the top results too to strip away the unwanted high bits there.

How to reach best performence with different data types?

I am working on custom class, which holds Date and Time. The main goal of that class is reaching the best performance. My target platform is Linux
Currently, I hold members like this
Year - int
Month - int
Day - int
Hour- int
Min - int
Sec - double (because I need milisecs as well).
What I am thinking now is too change types to following
Year - unsigned short
Month - unsigned char
Day - unsigned char
Hour- unsigned char
Min - unsigned char
Sec - unsigned char
Milisec - unsigned short
Which gives me 2 + 1 + 1 + 1 + 1 + 1 + 2 = 9 bytes.
As you already guessed I want to fit my class into 8 byte(there are no other members).
So what is the best approach to solve it, to merge(e. g. seconds and miliseconds) and use bit masks for retrieving values? Will it affect performance? And what if user passes integers to some setter, would type cast affect performance also ?
Thanks on advance.
There are multiple options you have here. The most compact way would be to have an integer timestamp. It would take a bit of processing to unpack it though. Another option is to use C++ bitfields to pack things tighter. For example, month only needs 4 bits, day 5 bits, minutes and seconds 6 bits. It should make things a bit slower, but only in theory. It all depends on the number of these dates you have and on the amount and kind of processing you're going to perform on them. In some cases having the struct tightly packed into bitfields would increase the performance because of higher memory throughput and better cache utilization. In other cases, the bit manipulation might become more expensive. Like always with performance, better not to guess, but measure.
The simpliest way here is to put pair of sec and millisec into one int (two bytes).
You don't need separate Sec (unsigned char) and Milisec (unsigned short), because you can put number from 0 to 60000 into one unsigned short.
Let's call it milliSecPack (unsigned short).
milliSecPack = 60 * Sec + Milisec;
And
Sec = milliSecPack / 1000;
Milisec = milliSecPack % 1000;

c++; Is bitset the solution for me?

I am writing a program and using memcpy to copy some bytes of data, using the following code;
#define ETH_ALEN 6
unsigned char sourceMAC[6];
unsigned char destMAC[6];
char* txBuffer;
....
memcpy((void*)txBuffer, (void*)destMAC, ETH_ALEN);
memcpy((void*)(txBuffer+ETH_ALEN), (void*)sourceMAC, ETH_ALEN);
Now I want to copy some data on to the end of this buffer (txBuffer) that is less than a single byte or greater than one byte, so it is not a multiple of 8 (doesn't finish on a whole byte boundary), so memcpy() can't be used (I don't believe?).
I want to add 16 more bits worth of data which is a round 4 bytes. First I need to add a value into the next 3 bits of txtBuffer which I have stored in an int, and a fourth bit which is always 0. Next I need to copy another 12 bit value, again I have this in an int.
So the first decimal value stored in an int is between 0 and 7 inclusively, the same is true for the second number I mention to go into the final 12 bits. The stored value is within the rang of 2^12. Should I for example 'bit-copy' the last three bits of the int into memory, or merge all these values together some how?
Is there a way I can compile these three values into 4 bytes to copy with memcpy, or should I use something like bitset to copy them in, bit at a time?
How should I solve this issue?
Thank you.
Assuming int is 4 bytes on your platform
int composed = 0;
int three_bits = something;
int twelve_bits = something_else;
composed = (three_bits & 0x07) | (1 << 3) | ((twelve_bits << 4) & 0xFFFFFF0);

Time Stamp and byte array

I'm trying to insert a timestamp (hour:min:sec) into a two-byte array and i'm a little confused on how to accomplish this...any help is greatly appreciated!
int Hour = CTime::GetCurrentTime().GetHour();
int Minute = CTime::GetCurrentTime().GetMinute();
int Second = CTime::GetCurrentTime().GetSecond();
BYTE arry[2];
//Need to insert 'Hour', 'Minute', & 'Second' into 'arry'
Thanks!
You can't. There are potentially 86402 seconds in a day (a day can have up to two leap seconds), but the 16 bits available to you in a byte[2] array can only represent 65536 separate values.
hour:min:sec is not what people call timestamp. A timestamp is the number of seconds elapsed since 1970-01-01 and will surely not fit into 16 bits.
Assuming ranges of hours=[0;24], minutes=[0;60], seconds=[0;60] (leap seconds included) you will need 5+6+6=17 bits which still won't fit into 16 bits.
If you had a 32-bit array, it would fit:
int Hour = CTime::GetCurrentTime().GetHour();
int Minute = CTime::GetCurrentTime().GetMinute();
int Second = CTime::GetCurrentTime().GetSecond();
uint8_t array[4];
// Just an example
*(uint32_t*)array = (Hour << 12) | (Minute << 6) | Second;
This sounds somewhat like homework for me... or what is the exact purpose of doing this?