Queue and trade-off with variable data size

Suppose you that I have two objects: One from “Serial” class and another from class “ProtocolDecoder”. The former having a “Task_SerialReader” that calls xQueueSend() when data arrives on port. The later has a “Task_Decoder” that blocks on xQueueReceive() and is responsible to decode the application protocol, checking the CRC and recovering the proper payload. Also, suppose that, in this example, the code section of Serial class reading the low level data cannot be changed, whats is: The data comes in a unknown length (from one byte to a known maximum limit MAX_DATA_SIZE). If a fixed size block is choosed
for queuing (ex. equal to that maximum limit), could be waste of space when data comes in blocks with small size:

struct _Block {
  size_t dataSize;
  uint8_t data[MAX_DATA_SIZE];
} Block;

In other hand, choosing dynamic allocation, there could be an overhead with many small size allocations:

struct _Block {
  size_t dataSize;
  uint8_t* pData;
} Block;

I think that an alternative solution, considering this scenario, would be have a queue with one byte element. After read the “unknown” size data block, the “Task_Reader” could call the xQueueSend() repeatedly in a loop for each byte. Similarly, the “Task_Decoder()” would recover one byte a time. Would this solution be any drawbacks related with performance or is perfectly acceptable with FreeRTOS?

On time: I’m aware that possibly the StreamBuffer API would be more suitable for the above application (unknown data size). However, my question is concerned specifically with queues.

If you have a single source and a single sink, then just pushing byte by byte onto the Queue, and reading byte by byte out of the queue is a perfectly workable solution. It is memory space efficient but will have a higher overhead than most solutions that move the data as a “block” (like the StreamBuffer, or a Queue of _Blocks).

I use something like this myself for most serial ports. I do plan to look at changing to a StreamBuffer, but I need to check on the effect of the use of TaskNotifications that StreamBuffers use, as many of my tasks also use TaskNotifications.

In my opinion a byte by byte queue would be CPU intensive and might eat up a lot on bandwidth. However, if this is the only thing to be done in the system, CPU really does not matter. Now, if that is the most efficient way, I guess not as a Fixed chunk size would be more optimal, however an optimal chunk size would be dependent on various factors like serial port internal HW FIFO size, data rate and allowed latency.

If your protocol allows you to decode the packet size from the protocol header, it has proven good practice to pre-process the packet in the lower level driver (ie read the header, then the entire frame, strip framing characters, verify crc etc) and then pass a complete frame to the next level.

This can even be done in an isr by preallocating a small number of max sized buffers; you can also use that strategy with a protocol like hdlc where packet size can only be deducted from the framing characters.

In my experience, some things that are major factors in the decision (and haven’t been given) are the character rate of the serial channel, and the amount of variation in packet size.

“Real” Serial channels with a “normal” serial bus (with characters rates up to order of 10k/second) are fine with a character by character buffering.

Higher speed links that tend to packetize the characters already, tend to prefer a more packetized buffer.

One big thing to watch out for is error recovery (like timeouts). This is much harder to implement in an ISR since you tend not to get an “interrupt” because nothing has been sent for a long period of time to trigger message timeouts.