In the embedded world we often have data structures that are passed around via fixed-length buffers. These are relatively easy to handle using something like this:
#define TOTAL_BUFFER_LENGTH 4096
struct overlay {
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
};
static_assert(sizeof(struct overlay) <= TOTAL_BUFFER_LENGTH);
struct overlay* overlay = malloc(TOTAL_BUFFER_LENGTH);
That is, we use a data structure as an overlay to allow easy access to the part of the buffer that is currently being used.
We have a number of buffer formats, however, that also use the last few bytes of the buffer to store things like checksums. We currently use constructions like this:
struct overlay {
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
char reserved[TOTAL_BUFFER_LENGTH -
sizeof(uint16_t) - sizeof(uint16_t) -
(sizeof(uint8_t) * ARY1_LEN) -
sizeof(uint32_t)];
uint32_t crc;
};
As ugly as this looks for this simple data structure, it's an absolute monstrosity when the structure grows to have dozens of fields. It's also a maintainability nightmare, as adding or removing a structure field means that the size calculation for reserved must be updated at the same time.
When the end of the structure only contains one item (like a checksum), then we sometimes use a helper function for reading/writing the value. That keeps the data structure clean and maintainable, but it doesn't scale well when the end of the buffer has multiple fields.
It would help greatly if we could do something like this instead:
struct overlay {
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
char reserved[TOTAL_BUFFER_LENGTH -
offsetof(struct overlay, reserved) -
sizeof(uint32_t)];
uint32_t crc;
};
Unfortunately, offsetof only works on complete object types and since this is in the middle of the definition of struct overlay, that type isn't yet complete.
Is there a cleaner, more maintainable way to do this sort of thing? I essentially need a fixed-length structure with fields at the beginning and at the end, with the remaining space in the middle reserved/unused.
In your situation, I think I'd probably do things this way:
typedef struct overlay_head
{
uint16_t field1;
uint16_t field2;
uint8_t array1[ARY1_LEN];
} overlay_head;
typedef struct overlay_tail
{
uint32_t crc;
} overlay_tail;
enum { OVERLAY_RSVD = TOTAL_BUFFER_LENGTH - sizeof(overlay_head)
- sizeof(overlay_tail) };
typedef struct overlay
{
overlay_head h;
uint8_t reserved[OVERLAY_RSVD];
overlay_tail t;
} overlay;
You can then work almost as before, except that where you used to write p->field1
you now write p->h.field1, and where you used to write p->crc you now write p->t.crc.
Note that this handles arbitrarily large tail structures quite effectively, as long as the head and tail both fit inside the overall size.
You could define a structure that simply has the buffer with a CRC field at the end:
struct checked_buffer {
char data[TOTAL_BUFFER_LENGTH - sizeof(uint32_t)];
uint32_t crc;
};
and then place your "overlays" on its data field. You're presumably already casting pointers to "convert" a raw buffer's char* into an overlay*, so it shouldn't be a big deal to cast from overlay* to checked_buffer* when you want to access the CRC field.
But if you want to have a field in a consistent position across a bunch of structures, it'd be easier to put it at the beginning of each structure. That way you can declare it directly in each structure without needing to do anything strange, and you don't need any pointer casts to access it.
How about that?
union a256
{
struct
{
int field_a;
int field_b;
char name[16];
//
int crcshadow;
};
struct
{
char buff[256-sizeof(int)];
int crc;
};
} ;
static_assert(offsetof(a256, crcshadow) < offsetof(a256, crc), "data too big");
The first struct contains data, the second define fixed size for this union.
Related
So I'm currently designing a simple transmission protocol for a ring network implemented in UART.
To transmit, I am converting the data from a struct to a char stream, started by < and terminated by > - I know of the possibility of any 0x3c or 0x3e value being mistaken for a < or > respectively, i am working on a solution to escape that. That's not part of my question.
So, the structure of it is something like <UINT32UINT32UINT8UINT8UINT16char[0...n]>, those types representing: <destinationId senderId TimeToLive Cmdtype CmdId Payloadlength Payload>. This always stays the same, so I can assume it without any delimiters between the values. This works, and I can theoretically also decode this. To easily access the bytes, I implemented the struct with unions:
typedef struct{
union{
uint32_t val;
char bytes[sizeof(uint32_t)];
} recipientId;
union{
uint32_t val;
char bytes[sizeof(uint32_t)];
} senderId;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} timeToLive;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} cmdType;
union{
uint8_t val;
char bytes[sizeof(uint8_t)];
} cmdId;
union{
uint16_t val;
char bytes[sizeof(uint16_t)];
} payloadLength;
char *payload;
char *commandRaw;
} aurPacket_t;
Once a packet exists, I decode it with something akin to this:
void decode_command(aurPacket_t packet){
if((packet.commandRaw[0] != '<' ) || (packet.commandRaw[strlen(packet.commandRaw) - 1] != '>') ){
printf("\n\nINVALID COMMAND\n\n");
}
else{
aurPacket_t newpacket;
// EITHER:
// for (int i = 0; i < strlen(newpacket.recipientId.bytes); i++){
// newpacket.recipientId.bytes[i] = (char)*(packet.commandRaw + 1 + i);
// }
// OR:
strncpy(newpacket.recipientId.bytes, (packet.commandRaw + 1), sizeof(newpacket.recipientId.bytes));
}
}
commandRaw contains the char stream that would be received in a message.
Using something like this I'd be able to do it, but I'd need to iterate it one by one since not all values are the same datatype - copying the string out to my payload in the end. Is there a way to make this more elegant than iterating through every single variable, to somehow iterate by using my struct as a guide for the iterator?
I was made aware of memcpy, but since I want to keep the protocol platform-independent, I'd prefer not to use it unless there is an elegant way to. Or is there a way to use it elegantly? Is my method of adding the variables even different from just using memcpy? Thinking about it, it doesn't seem like it would be, considering the ordering of the vars inside my struct. If I made a string containing <, memcpy-appended everything up to the payload, then memcpy-appended the payload, then appended a >, would there be any actual difference in the data? If not, I could just use that process in reverse to decode a message.
I'm currently encoding the message by using this function:
#define RAW_PACKET_LEN 1024
void parse_command(aurPacket_t packet){
snprintf(packet.commandRaw, RAW_PACKET_LEN, "<%.4s%.4s%.1s%.1s%.1s%.02s%.*s>",
packet.recipientId.bytes,
packet.senderId.bytes,
packet.timeToLive.bytes,
packet.cmdType.bytes,
packet.cmdId.bytes,
packet.payloadLength.bytes,
packet.payloadLength.val, packet.payload
);
} // memory for commandRaw allocated outside of function
which has the problem of not actually writing 0x00-bytes to the stream, but that's not part of the question - I'm looking for an answer to that at the moment (of course, if you know an easy fix, feel free to let me know :) )
The system is an ESP32 programmed using ESP-IDF.
Some tips here:
Your packet should not contain a pointer, it would point to an address that only the sender would know about. You want to actually copy the array into the command (see 4 below).
“char bytes[sizeof(uint8_t)]” is literally preprocessed at compile time into “char bytes[1]”, so you might as well just have “char byte”, which is singular.
If you are just using a union for a uint8_t and a char, don’t bother just cast it before you use it: (eg; printf(“%c”,(char)val);
You will save yourself a ton of headache if you can just agree on fixed packet size. It looks like the only thing that will be dynamic is the payload, and the commandraw. Pick your worst case and go with that. I know that you are using serial, but unless you are doing something other than 8N1 it won’t have checks anyway, so you don’t want anything too long. I suggest you pick a total length under 1472 incase you move to UDP/TCP one day.
Assuming you can do these things, copying the data out is a piece of cake. You make your command as a struct. Then you make a union which contains the command as the first member and an array the size of the command as the second member. For example I would used uint8s (you could use chars).
union CommandMsg{
//you could also define this ahead of time..inlining b/c lazy.
struct Command{
uint32_t recipientId;
uint32_t senderId;
uint8_t val;
char timeToLive; //why char?
uint8_t cmdType;
uint8_t cmdId;
uint16_t val;
uint16_t payloadLength; //maybe just strlen later if term'd?
char payload[256];
char commandRaw[256];
} asCommand;
uint8_t asByteArray[sizeof(Command)];
} commandMsg;
Safety first, before you stuff your command struct memcpy zeros into the whole thing. The zeros will act like terminators for any strings you memcpy later.
Make sure to __pack your struct
When you copy it back out cast your arrays to void pointer memcpy((void *)localCopy, (const void *)incoming, sizeof(CommandMsg));
Not really sure what counts as "elegant", but here are some diverse solutions.
The old-fashioned C way to deal with something like this would be to create a look-up table. Given that you only have a static "singleton" buffer static aurPacket_t aur_packet;, then you can create a pointer look-up table at compile-time:
#define AUR_PACKET_MEMBERS_N 6
static char* const AUR_PACKET_LOOKUP[AUR_PACKET_MEMBERS_N] =
{
aur_packet.recipientId.bytes,
aur_packet.senderId.bytes,
aur_packet.timeToLive.bytes,
aur_packet.cmdType.bytes,
aur_packet.cmdId.bytes,
aur_packet.payloadLength.bytes,
};
And now you can iterate over each part of the struct by accessing AUR_PACKET_LOOKUP[i] and get a char* to its bytes member.
A more radical (and not necessarily readable) approach would be "X macros", creating something like
#define AUR_PACKET_LIST \
X(UINT32, recipientId) \
X(UINT32, senderId) \
X(UINT8, timeToLive) \
X(UINT8, cmdType) \
X(UINT8, cmdId) \
X(UINT16, payloadLength) \
You can now generate the string at compile-time, without involving sprintf functions:
strcpy(packet.commandRaw,
#define X(type, member) #type
AUR_PACKET_LIST
#undef X
);
Which expands to strcpy(packet.commandRaw, "UINT32" "UINT32" ... and the pre-processor concatenates the string literals from there.
And finally, it's perfectly possible to go completely macro ape and use X macros to define the type itself too, which I don't really recommend unless you have severe requirements to avoid code repetition:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef uint8_t UINT8;
typedef uint16_t UINT16;
typedef uint32_t UINT32;
#define AUR_PACKET_LIST(X) \
/* type name */ \
X(UINT32, recipientId) \
X(UINT32, senderId) \
X(UINT8, timeToLive) \
X(UINT8, cmdType) \
X(UINT8, cmdId) \
X(UINT16, payloadLength) \
#define AUR_PACKET_DECL_MEMBER(type, name) \
union { \
type val; \
char bytes[sizeof(type)]; \
} name;
typedef struct{
AUR_PACKET_LIST(AUR_PACKET_DECL_MEMBER)
char *payload;
char *commandRaw;
} aurPacket_t;
int main(void)
{
aurPacket_t packet = {.commandRaw=malloc(256)};
packet.commandRaw[0]='\0';
#define AUR_PACKET_COMMANDRAW(type, name) #type
strcpy(packet.commandRaw, AUR_PACKET_LIST(AUR_PACKET_COMMANDRAW));
puts(packet.commandRaw);
return 0;
}
Your struct looks redundant. It's better to use something like this:
typedef struct {
uint8_t head;
uint32_t recipientId;
uint32_t senderId;
uint8_t timeToLive;
uint8_t cmdType;
uint8_t cmdId;
uint16_t payloadLength;
uint8_t *payload;
uint8_t tail;
} aurPacket_t;
Anyway you should deal with buffer to have memory for payload. It can be global array with fixed size, it's better for embedded software. memcpy is good solution when you controls all the sizes you operates with, because it's usually optimized for target hardware. You should use only fixed size types, like uint8_t and uint32_ for your data members in your packet to make it cross-platform and remember about big- or little-endian on your machines, when you storing data to packet.
Also it's good practice to make check sum a part of your packet to validate data.
I have the following struct definition:
typedef struct mb32_packet_t {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t *payload;
uint16_t checksum;
} __attribute__((packed)) mb32_packet_t;
Now I would like to have another union, so that I can get an uint8_t body[] pointer to the entire packet object. Something like this:
typedef struct mb32_packet_t {
union {
struct {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t *payload;
uint16_t checksum;
};
uint8_t body[?];
};
} __attribute__((packed)) mb32_packet_t;
The problem is that the payload field size is dynamically determined at runtime. Is there another way to accomplish this other than making payload fixed sized?
I basically want to send objects of this type through a network socket, so I need a uint8_t pointer that points to an object of this type. At the time of sending the object, I know the size of the entire object in bytes.
Introduction
The question is unclear, so I will discuss three apparent possibilities.
Fixed-length header followed by variable-length payload
A typical way to define a packet for a networking or messaging service is to have a fixed-length header followed by a variable-length payload. In modern C, the variable-length payload may be defined using a flexible array member, which is an array with no dimension at the end of a structure:
typedef struct
{
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
uint8_t payload[];
} mb32_packet_t;
Memory for such a structure is allocated use the base size provided by sizeof plus additional memory for the payload:
mb32_packet_t *MyPacket = malloc(sizeof *MyPacket + PayloadLength);
When you pass such an object to a routine that requires a char * or uint8_t * or similar type for its argument, you can simply convert the pointer:
SendMyMessage(…, (uint8_t *) MyPacket,…);
That cast, (uint8_t *) MyPacket, provides the pointer to the first byte of the packet requested in the question. There is no need to wedge another member into the structure or layer on a union or other declaration.
Prior to the introduction of flexible array members in C 1999, people would use one of two workarounds to create structures with variable amounts of data. One, they might just define a member array with one element and adjust the space calculations accordingly:
typedef struct
{
…
unsigned char payload[1];
} mb32_packet_t;
mb32_packet_t *MyPacket = malloc(sizeof *MyPacket + PayloadLength - 1);
Technically, that violated the C standard, since the structure contained an array of only one element even though more space was allocated for it. However, compilers were not as aggressive in their analysis of program semantics and their optimization as they are now, so it generally worked. So you may still see old code using that method.
Two, GCC had its own pre-standard implementation of flexible array members, just using an array dimension of zero instead of omitting a dimension:
typedef struct
{
…
unsigned char payload[0];
} mb32_packet_t;
Again, you may see old code using that, but new code should use the standard flexible array member.
Fixed-length header with pointer to variable-length payload
The payload-after-header form shown above is the form of packet I would most expect in a messaging packet, because it matches what the hardware has to put “on the wire” when sending bytes across a network: It writes the header bytes followed by the data bytes. So it is convenient to have them arranged that way in memory.
However, your code shows another option: The data is not in the packet but is pointed to by a pointer in the packet, with uint8_t *payload;. I would suspect that is a mistake, that the network or messaging service really wants a flexible array member, but you show it followed by another member, uint16_t checksum. A flexible array member must be the last member in a structure, so the fact that there is another member after the payload suggests this definition with a pointer may be correct for the messaging service you are working with.
However, if that is the case, it is not possible to get a pointer to the complete packet object, because the object is in two pieces. One contains the header, and the other, at some unrelated location in memory, contains the data.
As above, you can produce a uint8_t * pointer to the start of the packet with (uint8_t) MyPacket. If the messaging system knows about the pointer in the structure, that should work. If you have mistaken what the packet structure must be, it will fail.
Fixed-length header followed by fixed-length payload space
Code elsewhere on Stack Overflow shows a struct mb32_packet_t with a fixed amount of space for a payload:
typedef struct mb32_packet_t {
uint8_t compid;
uint8_t servid;
uint8_t payload[248];
uint8_t checksum;
} __attribute__((packed)) mb32_packet_s;
In this form, the packet is always a fixed size, although the amount of space used for the payload could vary. Again, you would obtain a uint8_t * pointer to the packet by a cast. There is no need for a special member for that.
This is possible, but not with a struct or union, because all parts of a struct or union need to have a known size. You can still use a struct for the header.
Because the body starts at a known location, there's a trick you can use to access it as if it was part of the structure. You can declare it with no size at all (a "flexible array member") or as 0 bytes (a GCC extension that predates the standard). The compiler will not allocate any space for it, but it will still let you use the name to refer to the end of the struct. The trick is that you can malloc extra bytes after the end of the struct, and then use body to refer to them.
typedef struct mb32_packet_t {
union {
struct {
uint16_t preamble;
uint8_t system_id;
uint8_t message_id;
uint8_t reserved;
uint32_t paylen;
};
uint8_t header[9];
};
uint8_t body[]; // flexible array member
} __attribute__((packed)) mb32_packet_t;
// This is not valid. The body is 0 bytes long, so the write is out of bounds.
mb32_packet_t my_packet;
my_packet.body[0] = 1;
// This is valid though!
mb32_packet_t *my_packet2 = malloc(sizeof(*my_packet2) + 50);
my_packet2->body[49] = 1;
// Alternative way to calculate size
mb32_packet_t *my_packet3 = malloc(offsetof(mb32_packet_t, body[50]));
my_packet3->body[49] = 1;
The flexible array member must be last. To access the checksum, you will need to allocate an extra 2 bytes, and use pointer arithmetic. Fortunately, this is just for the checksum, and not the entire header.
mb32_packet_t *my_packet = malloc(sizeof(*my_packet) + body_size + 2);
uint16_t *pchecksum = (uint16_t*)&my_packet.body[body_size];
// or
uint16_t *pchecksum = (uint16_t*)(my_packet.body + body_size);
After you fill in the header, body and checksum, then because they are contiguous in memory, a pointer to the header is also a pointer to the entire packet object.
I usually do it this way:
typedef struct
{
size_t payload_size;
double x;
char y[45];
/* another members */
unsigned char payload[];
}my_packet_t;
or if your compiler does not support FAMs
typedef struct
{
size_t payload_size;
double x;
char y[45];
/* another members */
unsigned char payload[0];
}my_packet_t;
So it the payload can be at the end of the header structure
I want to calculate the CRC value of some data in STM32 micro controller.
The HAL function to calculate the CRC has the following footprint:
uint32_t HAL_CRC_Calculate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength);
My data are stored in a struct:
struct caldata_tag {
float K_P_Htng;
uint16_t K_I_Htng;
uint16_t K_D_Htng;
uint16_t K_P_Coolg; } caldata;
Who is the safest and appropriate way to pass the struct to the HAL_CRC_Calculate() function?
I am thinking about this :
#define U32BUFFERSIZE sizeof(struct caldata_tag)/sizeof(uint32_t)
uint32_t buffer[U32BUFFERSIZE];
uint32_t crcValue;
/* calculate the crc value of the data */
memcpy(buffer,&localStruct,U32BUFFERSIZE);
crcValue = HAL_CRC_Calculate(&CrcHandle,buffer,U32BUFFERSIZE);
but I am thinking that is an ugly way, could you tell me if it is ok? OR if you have a better idea?
Who is the safest and appropriate way to pass the struct to the HAL_CRC_Calculate()function?
Challenges:
HAL_CRC_Calculate() apparently wants to calculate the CRC based on multiples of uint32_t.
The size of struct caldata_tag may not be a multiple of the size of uint32_t.
struct caldata_tag may contain padding of an unknown state in caldata.
Use a union of struct caldata_tag and a large enough uint32_t array. Zero it, copy the members and then calculate the CRC.
I am thinking that is an ugly way, could you tell me if it is ok? OR if you have a better idea?
Form a helper function.
// Find the quotient of sizeof caldata_tag / sizeof(uint32_t), rounded up
#define U32BUFFERSIZE ((sizeof(struct caldata_tag) + sizeof(uint32_t) - 1)/sizeof(uint32_t))
uint32_t caldata_CRC(CRC_HandleTypeDef *hcrc, const struct caldata_tag *p) {
// u's size will be a multiple of sizeof uint32_t
union {
uint32_t u32[U32BUFFERSIZE];
struct caldata_tag tag;
} u = { {0} }; // zero every thing
// copy the members, not the padding
u.tag.K_P_Htng = p->K_P_Htng;
u.tag.K_I_Htng = p->K_I_Htng;
u.tag.K_D_Htng = p->K_D_Htng;
u.tag.K_P_Coolg = p->K_P_Coolg;
return HAL_CRC_Calculate(hcrc, u.u32, U32BUFFERSIZE);
}
Use
uint32_t crcValue = caldata_CRC(&CrcHandle, &caldata);
[Update]
Further research indicates that the BufferLength is a count of uint8_t, uint16_t, uint32_t depending on hcrc->InputDataFormat. OP has not provided that, yet if that can be set to uint8_t. then code only needs to worry about padding in struct caldata.
#define U8BUFFERSIZE sizeof(struct caldata_tag)
uint32_t caldata8_CRC(CRC_HandleTypeDef *hcrc, const struct caldata_tag *p) {
// u's size will be a multiple of sizeof uint32_t
union {
uint32_t u32[U32BUFFERSIZE];
struct caldata_tag tag;
} u = { {0} }; // zero every thing
// copy the members, not the padding
u.tag.K_P_Htng = p->K_P_Htng;
u.tag.K_I_Htng = p->K_I_Htng;
u.tag.K_D_Htng = p->K_D_Htng;
u.tag.K_P_Coolg = p->K_P_Coolg;
return HAL_CRC_Calculate(hcrc, u.u32, U8BUFFERSIZE);
}
If the compiler allows __attribute__((__packed__)), #sephiroth answer is a good way to go.
You can use a pointer that points directly to the beginning of the struct, without having to use the support buffer:
uint32_t *p = (uint32_t*)&localStruct;
There are 2 problems with this:
The first one is that you might get unexpected results if the compiler is doing padding on the struct; you can solve this by adding the (packed) attribute to the struct to tell the compiler not to do any padding
struct __attribute__((__packed__)) caldata_tag {
//...
}
The other problem is that your structure size isn't a multiple of 32, so it can't be represented in an array of uint32_t without having 16 random bits at the end of the last element. The same goes for your example, bur i think in this case you are discarding the last element of buffer because U32BUFFERSIZE should be equal to 2, so you are ignoring K_P_Coolg and the 16 random bits beside it when calculating the crc.
My suggestion when working with crc and stuff like that is using 8 bit buffers instead of 32 bit ones, as it completely eliminates the latter problem.
No workaround is needed. According to the documentation for the function HAL_CRC_Calculate: "By default, the API expects a uint32_t pointer as input buffer parameter. Input buffer pointers with other types simply need to be cast in uint32_t and the API will internally adjust its input data processing based on the handle field hcrc->InputDataFormat."
So set the field correctly in the first parameter and you can pass a pointer to bytes to the function.
So I have some structs containing data that I want to send to another process using a unix socket. This process may not be compiled using the same compiler version, or even be written in C for that matter. This is the struct (note that some stuff is commented out):
struct nested_struct {
uint8_t a;
uint8_t b;
uint16_t c;
} */__attribute__((packed))*/;
struct my_struct {
uint32_t num_nested_structs;
/* uint8_t padding[3];*/
uint8_t x;
uint16_t y;
uint16_t z;
struct nested_struct nested[];
} /*__attribute__((packed))*/;
For convenience and performance, I'd like to get away with something like
write(socket, &data.x, data.num_nested_structs * sizeof(struct nested_struct) + 5)
or something -- but I doubt this would be safe, given that struct my_struct is not nicely aligned. But how about if we un-comment the packed attribute? This feels like it should work, but I've read that referencing fields in __packed__ structs by address can be dangerous.
What if we instead uncomment the uint8_t padding[3]; field? Now both structs are word size-aligned (on a system with WORD_BIT = 32). Is it safe to assume that the compiler won't add any padding in this case? If so, is this enough to ensure that accessing 5 + 4*num_nested_structs bytes of memory starting from &my_struct.x is safe?
I'm programming for embedded, resource constrained devices in C.
I have a structure, like this:
typedef struct UjThread{
struct{
UInt32 runInstr;
UInt8* mailbox;
}appBucket;
struct{
UInt32 appId;
UInt32 numInstr;
UInt32 allocMem;
UInt32 eepromStartAddr;
}appContract;
UInt16 spBase; //we use an empty ascending stack
UInt16 spLimit; //also used for "isPtr"
UInt16 localsBase;
UInt32 stack[];
}UjThread;
I start a thread per object and allocate the needed memory (92 bytes for this structure, but I haven't shown all fields).
However, some objects won't use the internal appContract and appBucket structures but memory for those structures will still be allocated.
Is there a way to avoid this? To designate internal structures as optional or perhaps extract the size of those internal structures and subtract it from the memory allocation?
I could make two separate structures, one per type of object, but I'd prefer not to since I'd have to adjust my code everywhere to work with the two types of threads.
Besides the obvious - using two structs, I see only two other possibilities.
Either use a pointer to a separately allocated appContract, or if some of the data you need is mutually exclusive, use a union.
Consider this implementation of single inheritance that works in C.
Define a base struct that contains all the elements common to both objects. Notice that I've changed the type of the stack member to a pointer because that's going to have to be a separate allocation in this design.
typedef struct ThreadBase{
UInt16 spBase; //we use an empty ascending stack
UInt16 spLimit; //also used for "isPtr"
UInt16 localsBase;
UInt32 *stack;
}ThreadBase;
Then declare another struct that contains the base object as the first member and appends the extra stuff.
typedef struct ThreadExtra{
ThreadBase base;
struct{
UInt32 runInstr;
UInt8* mailbox;
}appBucket;
struct{
UInt32 appId;
UInt32 numInstr;
UInt32 allocMem;
UInt32 eepromStartAddr;
}appContract;
}ThreadExtra;
Now you can define a ThreadBase object for threads that only need the base stuff. And you can define a ThreadExtra object for the threads that need more. But you can cast the ThreadExtra object to ThreadBase because ThreadBase is the first member of ThreadExtra. So in general purpose code that doesn't deal with the ThreadExtra elements you can treat all the Thread objects as if they are ThreadBase objects.
If your optional fields are at the beginning of your struct, you can adjust the address of an allocated object, so the optional fields reside in unallocated memory. Use the offsetof macro to determine where the mandatory data starts:
offsetof(UjThread, spBase) // in bytes
Adjust the allocation size by this amount:
UjThread *newThread;
if (NoOptionalFields())
{
size_t sizeReduce = offsetof(UjThread, spBase);
size_t size = sizeof(UjThread) - sizeReduce;
newThread = (void*)((char*)malloc(size) - sizeReduce);
}
else
{
newThread = malloc(sizeof(UjThread));
}
To free the memory, don't forget to adjust the pointer back:
if (NoOptionalFields())
{
size_t sizeReduce = offsetof(UjThread, spBase);
free((char*)newThread + sizeReduce);
}
else
{
free(newThread);
}
BTW since you have a "flexible array member" in your struct, the actual size calculation is more complicated than in my example. But you get the idea - just subtract the size of the optional fields from both the allocation size and the resulting pointer.
If the stack had a fixed size, you could use the idiomatic C-style single inheritance:
typedef struct {
int a;
} Base;
typedef struct {
Base base;
int b;
} Derived;
void useBase(Base *);
void test(void) {
Base b;
Derived d;
useBase(&b);
useBase(&d.base); // variant 1
useBase((Base*)&d); // variant 2
}
Alas, the stack doesn't have a fixed size, so the somewhat idiomatic if unnecessarily shaky variant 2 won't work, but variant 1 will:
typedef struct {
int a[];
} Small;
typedef struct {
int b;
Small small;
} Large;
void useBase(Base *);
void test(void) {
Small s;
Large l;
useBase(&s);
useBase(&l.small);
}