Parsing dei pacchetti tramite struct

luca 18/01/2018 4

Analizzando il codice sorgente di un programma opensource, ho trovato una interessante tecnica per effettuare il parsing dei dati ricevuti in maniera molto efficente, utilizzando una struct.

Questa tecnica funziona se i dati ricevuti hanno lunghezza e struttura fissa, ad esempio si applica molto bene a pacchetti dati.

In fase di ricezione, normalmente il programma memorizza i dati ricevuti in un buffer all’interno della memoria RAM. Ipotizziamo che il pacchetto abbia una lunghezza di 14 bytes:

char rx_buffer[14];

Le specifiche prevedono che tale pacchetto sia formato dai seguenti campi:

  • indirizzo, 5 bytes
  • valore di temperatura, 3 bytes
  • valore di umidità, 3 bytes
  • contatore di trasmissione (TX), 1 bytes
  • checksum, 2 bytes

Possiamo definire una struct che rappresenta la struttura del pacchetto:

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

La tecnica consiste nell’applicare, come se fosse un template, la struct al buffer:

mem-struct-001

in tal modo i bytes ricevuti vengono automaticamente suddivisi nei vari campi:

mem-struct-002

Per effettuare il parsing è quindi sufficiente effettuare un cast del buffer:

my_packet* parsed_data = (my_packet*)rx_buffer;

e accedere ai dati come campi di tale struct:

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

Padding e packed

Abbiamo però un problema: per rendere più efficiente l’accesso alla memoria, i compilatori normalmente allineano le variabili in base alla loro dimensione. Questo significa che – quando viene allocato in memoria spazio per la struct – è possibile che tra le variabili vi sia dello spazio “libero” (chiamato padding):

mem-struct-003

La presenza del padding causa un disallineamento se applichiamo la struct al buffer con i dati ricevuti (che invece sono continui). Fortunatamente è possibile richiedere al compilatore di non inserire padding utilizzando l’attibuto packed:

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;

In questo modo la tecnica mostrata ad inizio articolo funziona perfettamente!

4 Comments »

  1. whm 18/01/2018 at 09:01 - Reply

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

    parsed_data->address

    instead of

    parsed_data.address

    • luca 18/01/2018 at 09:24 - Reply

      you’re absolutely right! thanks

  2. francesco 18/01/2018 at 15:42 - Reply

    mi sembra una valida alternativa a json

  3. Federico 05/04/2018 at 14:51 - Reply

    Veramente interessante, grazie!

Leave A Response »

Questo sito usa i cookie per poterti offrire una migliore esperienza di navigazione maggiori informazioni

Questo sito utilizza i cookie per fonire la migliore esperienza di navigazione possibile. Continuando a utilizzare questo sito senza modificare le impostazioni dei cookie o clicchi su "Accetta" permetti al loro utilizzo.

Chiudi