3

I'm currently writing an abstraction layer for RF24Mesh microcontroller layer to implement message signing.

As I want to make it as universal as possible I'd love to make it possible to pass any struct/payload for ex: (This is only an example of one struct that one should be able to pass to the function)

  struct payload_sensor {
        uint8_t some_other_sensor_data[32];
        int sensor_id;
        int sensor_data;
  };

To the signing function and then let it deal with storing the payload/struct until everything else is done for it to be sent.

But here's the thing, I can't predict the exact size of the struct passed to the function (it should deal with any struct passed to it), the same struct might be reused (filled with other data) and passed to the function again. I'm totally unable to think of a good solution.

Now how do I implement a proper buffer to store these unknown payloads/structs until they are ready to be sent? I'd also have to keep in mind that the code is running on AVR, so AVR memory (fragmentation) might become an issue.

I'm kind of new to structs and more advanced stuff, so don't mind me missing something obvious please.

3
  • 2
    If it's unknown then why does it need to be a struct? Pass a char* or void* and a size. Commented Apr 26, 2016 at 21:08
  • Send as a TLV (type, length, value). When both sides agree what a T defines, you don't even need length. Commented Apr 26, 2016 at 21:14
  • Pass a buffer that has a whatever struct memcpyed into it. Have the first struct byte/short/int by convention hold a tag that tells the other end what type is coming along. Commented Apr 26, 2016 at 21:17

3 Answers 3

3

Often this sort of problem is solved by defining your struct such as:

struct payload_sensor {
    unsigned int struct_size;
    int sensor_id;
    type any_other_always_present_data;
    char variable_data[];
};

where the variable_data is the unknown part of your payload.

When you know how large the actual variable_data needs to be, you can do something like:

unsigned int actual_length = length_of_variable_data + sizeof(struct payload_sensor);
struct payload_sensor *myStruct = malloc(actual_length);
myStruct->struct_size = actual_length;

and to copy your variable data to the structure you would:

memcpy(&myStruct->variable_data, &data_source, length_of_variable_data);

Based on comments and edits, it sounds like your intention is to be able to pass data to some function that must hold on to a copy of the data until some later point in time, and that function is unaware of the content it's being asked to hold. If this is the case, all you need to do is tell the function the length with each call. For example, void myFunction(const void *buffer, unsigned int length);:

myFunction(&myStructOfType1, sizeof(myStructOfType1));
myFunction(&myStructOfType2, sizeof(myStructOfType2));

and, based on your concern of the structures being reused, have myFunction() copy length bytes starting at buffer to wherever it is storing things (as well as recording the length it stored at the copy's address) for future processing.

Sign up to request clarification or add additional context in comments.

7 Comments

So basically I would still have to limit the input to a certain struct (what data can be changed) in order to store it somewhere else before it's sent?
You're limiting the input to begin with a certain struct, but the struct_size variable in that certain struct is there so you can go beyond the structure and track how much data you've gone beyond. This is necessary if you wish to keep the fixed and variable components in one place in memory (to, for example, be able to transmit the data all at once). If you have well defined sets of variable data, you should look into using union inside your struct.
Although I think is more correct and readable to use flexible array member instead of array with zero elements (which I'm not exactly sure if it's standard).
@Avamander I would suggest you use malloc() to allocate your copy space. You still need to be able to store your metadata: the pointers to each buffer and the length of each buffer. Perhaps a linked list of struct meta { void *buffer; unsigned int length; struct meta *next; } and a static struct meta *stored_buffers; to find the first one with. Further details are too chatty for stack overflow though.
I'm not sure how to start a chat until the "too many comments" link comes up. If your malloc implementation does not handle the alignment issues you can do it yourself by rounding up the size of your allocation as needed, and storing into an aligned offset based on the block you get. As to fragmentation issues - how would you normally solve that? If you don't have a platform API for it you may need to write your own (relatively simple) allocator that operates on a large bit of memory you've set aside for the problem.
|
2

There is no such a thing like "unknown sized structures" in C. sizeof(payload_sensor) will be everytime the same size.

3 Comments

That struct will indeed be the same size, but when someone passes another struct to the function that I do not know anything of?
It's impossible to pass object with another type unless someone will cast it via (payload_sensor *) pointer-to-wrong-object. But it is an undefined behavior, and you can don't take care about this situation because even compiler don't :)
payload_sensor was just an example of a struct that would be passed to the function.
1

You can pass a char[]buffer into the function that has a whatever struct memcpyed into it. This gets rid of the function signature based on struct problem.

Have the first struct byte/short/int of all structs you pass around by convention hold a tag that tells the other end what type is coming along.

If the messages are sent over a wire (UART, for example), watch out for byte order and different sizeof() issues between platforms which could render basing your protocol on structs a vane attempt.

If you are exchanging cross-platform messages (between PC and AVR, for example), you really don't want to deal with compiler incompatibilities. In such a case, define and document a protocol for the wire wich is not based on a C struct and put the bytes where they belong manually in an unsigned char buffer. Or use an ASCII-based protocol.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.