2009-02-09 12 views
4

J'écris un programme en C pour Linux sur un processeur ARM9. Le programme consiste à accéder à des paquets de réseau qui comprennent une séquence de données étiquetées comme:Sûr, moyen efficace d'accéder à des données non alignées dans un paquet réseau à partir de C

<fieldID><length><data><fieldID><length><data> ... 

Les champs fieldID et de longueur sont à la fois uint16_t. Les données peuvent être de 1 octet ou plus (jusqu'à 64 Ko si la longueur totale a été utilisée, mais ce n'est pas le cas).

Tant que <data> a un nombre pair d'octets, je ne vois pas de problème. Mais si j'ai une section <data> de 1 ou 3 ou 5 octets, alors le champID de 16 bits suivant ne se retrouve pas sur une limite de 16 bits et j'anticipe des problèmes d'alignement. Cela fait longtemps que je n'ai rien fait de tel, alors je ne suis pas sûr des détails. Tous les commentaires sont les bienvenus. Merci.

Répondre

6

Pour éviter les problèmes d'alignement dans ce cas, accédez à toutes les données sous la forme unsigned char *. Alors:

unsigned char *p; 
//... 
uint16_t id = p[0] | (p[1] << 8); 
p += 2; 

L'exemple ci-dessus suppose la mise en page de données « peu endian », où octet le moins significatif vient en premier dans un certain nombre multi-octets.

1

L'alignement va toujours bien se passer, mais peut-être pas super efficace, si vous passez par un pointeur d'octets.

Mis à part les problèmes d'endian-ness, vous pouvez memcpy du pointeur «réel» octets dans tout ce que vous voulez/besoin qui est correctement aligné et vous serez bien.

(cela fonctionne parce que le code généré chargera/stockera les données comme des octets, ce qui est sans danger pour l'alignement) lorsque l'assembly généré contient des instructions de chargement et de stockage de 16/32/64 bits de mémoire. tout s'écroule).

+0

Mettre de côté l'endian-ness est déconseillé dans un réseau. –

4

Le moyen facile est de reconstruire manuellement les uint16_t s, au détriment de la vitesse:

uint8_t *packet = ...; 
uint16_t fieldID = (packet[0] << 8) | packet[1]; // assumes big-endian host order 
uint16_t length = (packet[2] << 8) | packet[2]; 
uint8_t *data = packet + 4; 
packet += 4 + length; 

Si votre processeur prend en charge, vous pouvez taper-jeu de mots ou d'utiliser un syndicat (mais méfiez-vous de strict aliasing).

uint16_t fieldID = htons(*(uint16_t *)packet); 
uint16_t length = htons(*(uint16_t *)(packet + 2)); 

Notez que l'accès non alignés ne sont pas toujours pris en charge (par exemple, ils pourraient générer une faute de quelque sorte), et sur d'autres architectures, ils sont pris en charge, mais il y a une pénalité de performance.

Si le paquet est pas aligné, vous pouvez toujours le copier dans un tampon statique, puis le lire:

static char static_buffer[65540]; 
memcpy(static_buffer, packet, packet_size); // make sure packet_size <= 65540 
uint16_t fieldId = htons(*(uint16_t *)static_buffer); 
uint16_t length = htons(*(uint16_t *)(static_buffer + 2)); 

Personnellement, je vais juste pour l'option # 1, car il sera le plus portable.

+0

Ma recommandation serait de décoder les paquets en écrivant certaines fonctions qui acceptent un char * et retournent un entier signé ou non signé de 16 ou 32 bits, et codent des paquets en utilisant une paire de fonctions qui acceptent un char * et un 16- ou 32 entier -bit. – supercat

4

Vous devriez avoir des fonctions (en ligne et/ou si la langue templated que vous utilisez prend en charge ces fonctions) qui lit les données potentiellement non alignés et retourner le type de données que vous êtes intéressé par quelque chose comme:.

uint16_t unaligned_uint16(void* p) 
{ 
    // this assumes big-endian values in data stream 
    // (which is common, but not universal in network 
    // communications) - this may or may not be 
    // appropriate in your case 

    unsigned char* pByte = (unsigned char*) p; 

    uint16_t val = (pByte[0] << 8) | pByte[1]; 

    return val; 
} 
+0

Typo dans le type de retour, devrait être uint16_t.Sinon, super fonction, je l'ai juste utilisé pour résoudre un problème d'alignement sur un UltraSparc64 et ça a bien fonctionné. +1 – bortzmeyer