Parsing a packet using structs

by luca
4 comments

When I was analyzing the source code of an opensource software, I found an interesting technique to parse incoming data in a very efficient way, using a struct.

This technique works if the received data has a fixed length and structure; for example it applies very well to data “packets”.

When receiving, the program normally stores incoming data in a buffer in the RAM memory. For example let’s assume that the packet has a length of 14 bytes:

char rx_buffer[14];

Your specific protocol defines the following fields in the packet:

[checklist]

  • address, 5 bytes
  • temperature value, 3 bytes
  • humidity value, 3 bytes
  • TX counter, 1 bytes
  • checksum, 2 bytes

[/checklist]

You can define a struct that represents the structure of the packet:

typedef struct {
  uint8_t address[5];
  uint8_t temperature[3];
  uint8_t humidity[3];
  uint8_t tx;
  uint8_t checksum[2];
} my_packet;

The technique consists in applying, as if it were a template, the struct to the buffer:

mem-struct-001

in this way the received bytes are automatically divided into the different fields:

mem-struct-002

Therefore, to parse the data you only need to cast the buffer:

my_packet* parsed_data = (my_packet*)rx_buffer;

and access the struct fields:

parsed_data->address;
parsed_data->temperature;
[...]

Padding and packed

You still have a problem: to make it more efficient to access the memory, compliers normally align the variables based on their size. This means that – when a compiler allocates memory for your struct – some empty space (named padding) could be added between the different variables:

mem-struct-003

The presence of the padding space causes a misalignment if you apply the struct to the buffer with the received data (which are instead continous). Fortunately, it is possible to ask the compiler not to insert padding using the packed attribute:

typedef struct {
  uint8_t address[5];
  uint8_t temperature[3];
  uint8_t humidity[3];
  uint8_t tx;
  uint8_t checksum[2];
}__attribute__((packed)) my_packet;

If you remember to add this attribute, the technique works perfectly!

Related Posts

4 comments

whm Thursday January 18th, 2018 - 09:01 AM

Isn’t parsed_data a pointer? To access the struct field, shouldn’t you use

parsed_data->address

instead of

parsed_data.address

Reply
luca Thursday January 18th, 2018 - 09:24 AM

you’re absolutely right! thanks

Reply
francesco Thursday January 18th, 2018 - 03:42 PM

mi sembra una valida alternativa a json

Reply
Federico Thursday April 5th, 2018 - 02:51 PM

Veramente interessante, grazie!

Reply

Leave a Comment

2 − 1 =