Код
typedef uint32_t ba05_blockKey[BA05_BLOCKKEYSIZE];
//заголовок у обоих типов пакетов общий, поэтому опишем его один раз
struct ba05_net_pktHeader
{
uint16_t packetType : 3; //ID-пакет, BlockPacket или HeaderPacket
uint16_t connStageID : 3; //public_keys_interchange, random_sample_interchange, hash_interchange
uint16_t needACK : 1; //сообщает приёмнику, что в ответ на этот пакет нужно обязательно отправить ACK пакет
uint16_t ACK : 1; //Признак пакета подтверждения. Если ACK==1 - все остальные поля не используются(кроме crc8,естественно)
uint16_t crc8 : 8;
};
//описание поля data для пакета IDPacket
struct ba05_net_IDPacket_data
{
ba05_ID public_ID;
ba05_ID corp_ID;
};
//поле data для Block-пакета описано в объявлении ba05_net_packet ниже
typedef struct
{
struct ba05_net_pktHeader header;
union
{
struct ba05_net_IDPacket_data asIDPacket;
ba05_blockKey asBlockPacket;
}data;
}ba05_net_packet;
Прагмы, заставляющей компилятор запаковать структуру - нет.
Тип ba05_blockKey может изменяться в зависимости от настроек алгоритма. Т.е. вместо uint32_t там может появиться uint16_t или uint64_t, соответственно, выравнивание будет разным.
На данный момент, не сложно догадаться, после header идёт пустышка(1слово), длее идёт data.
Если бы структура передавалась всегда полностью - проблем бы не было. Но в зависимости от ситуации, передаётся либо только header, либо header + asIDPacket, либо целиком(header + asBlockPacket).
Функции отправки/приёма анализируют header.packetType и по нему определяют сколько байт нужно отправить/принять(а также обработать при пересчёте CRC).
И всё было-бы хорошо, если бы не тот факт, что из-за пустышек, применяемых для выравнивания, сумма sizeof всех полей получается меньше sizeof всей структуры.
Как я уже говорил, настройки могут меняться и нужно это дело как-то обработать универсальным способом.
На данный момент я сделал так:
Код
*/
* Ниже, я постарался обработать эту ситуацию с помощью препроцессора и арифметики с указателями.
* Суть в том, что вместо sizeof я применял разность адресов .data и .header
* оператор взятия адреса(&) позволит нам уловить момент вставки компилятором пустышки
* пример: <header size=1byte,address=0><пустышка size=1byte,address=1><data size=2,address=2>
* если просто взять sizeof(header) + sizeof(data) байт и отправить - то мы отправим header полностью
* пустышку полностью, а поле data будет передано не полностью(на sizeof(пустышка) байт меньше)
* Но если я вместо sizeof(header) использую (&data - &header) то пустышка будет учтена.
*/
enum { HeaderPacketSize = sizeof(((ba05_net_packet *)0)->header) }; //header сам по себе никуда не выравнивается
//а дальше пришлось мудрить
#define BlockPacketSize ( ((int)&((ba05_net_packet *)0)->data.asBlockPacket - (int)&((ba05_net_packet *)0)->header) + sizeof(((ba05_net_packet *)0)->data.asBlockPacket) )
#define IDPacketSize ( ((int)&((ba05_net_packet *)0)->data.asIDPacket - (int)&((ba05_net_packet *)0)->header) + sizeof(((ba05_net_packet *)0)->data.asIDPacket) )
//теперь нам нужно правильно определить размер в байтах
#ifdef SIZEOF_IS_IN_WORDS
enum { HeaderPacketSize_bytes = HeaderPacketSize * 2 };
#define BlockPacketSize_bytes ( BlockPacketSize * 2 )
#define IDPacketSize_bytes ( IDPacketSize * 2 )
#elif defined(SIZEOF_IS_IN_BYTES)
enum { HeaderPacketSize_bytes = HeaderPacketSize };
#define BlockPacketSize_bytes ( BlockPacketSize )
#define IDPacketSize_bytes ( IDPacketSize )
#endif
Работает. Но как-то мудрёно получается....
Прокомментируйте решение, пожалуйста. Что можно поменять? Как сделать проще/лучше?
P.S. SIZEOF_IS_IN_WORDS/SIZEOF_IS_IN_BYTES тут присутствуют для обеспечения портируемости. Дело в том, что мой компилятор возвращает sizeof в словах(по 2 байта). Побайтового доступа к памяти нет.
Однако, при работе с сетью нам нужны байты. На других архитектурах, где sizeof() ведет себя прилично - этой проблемы не будет. Поэтому я обрабатываю эту ситуацию отдельно.
А функции отправки/приёма просто могут использовать *_bytes и не беспокоиться не о чём
The truth is out there...