2 votes

Pourquoi la première donnée emballée dans le struct est little endian, mais le reste est big endian ?

import struct
port = 1331
fragments = [1,2,3,4]
flags = bytes([64])
name = "Hello World"

data = struct.pack('HcHH', port, flags, len(fragments), len(name))

print(int.from_bytes(data[3:5], byteorder='big'))
print(int.from_bytes(data[5:7], byteorder='big'))
print(int.from_bytes(data[0:2], byteorder='little'))

Quand je les imprime comme ça, ils sortent correctement. Il semble que le port est en little endian, alors que len(fragments) y len(name) sont en big endian. Si je fais aussi big endian sur le port, il obtient la mauvaise valeur.

Alors pourquoi la structure se comporte-t-elle ainsi ? Ou est-ce que quelque chose m'échappe ?

2voto

Demi-Lune Points 832

Il y a un drôle d'alignement qui se produit à cause du 'c' au milieu du 'H'. Vous pouvez le voir avec calcsize :

>>> struct.calcsize('HcHH')
8
>>> struct.calcsize('HHHc')
7

Donc vos données ne sont pas alignées comme vous le pensiez. Le déballage correct est :

print(int.from_bytes(data[4:6], byteorder='little'))
# 4
print(int.from_bytes(data[6:], byteorder='little'))
# 11

Il s'avère que par hasard, l'octet ajouté du 'c' est ' \x00 ', et a rendu votre chaîne d'octets correcte en big-endian :

>>> data
b'3\x05@\x00\x04\x00\x0b\x00'
        ^^^^
        this is the intruder

0voto

Mad Physicist Points 3218

Par défaut, votre appel à pack est équivalent à ce qui suit :

struct.pack('@HcHH', port, flags, len(fragments), len(name))

Le résultat ressemble à ceci (imprimé avec '.'.join(f'{x:02X} for x in data') ):

33.05.40.00.04.00.0B.00
 0  1  2  3  4  5  6  7

Le nombre 4 est codé dans les octets 4 et 5, en little endian, et 11 est codé dans les octets 6 et 7. L'octet 3 est un octet de remplissage, inséré par pack pour aligner correctement les éléments suivants short s sur une frontière paire.

Par le docs :

Note Par défaut, le résultat de l'empaquetage d'une structure C donnée inclut des octets de remplissage afin de maintenir un alignement correct pour les types C impliqués ; de même, l'alignement est pris en compte lors du dépaquetage. Ce comportement est choisi pour que les octets d'une structure empaquetée correspondent exactement à la disposition en mémoire de la structure C correspondante. Pour gérer les formats de données indépendants de la plate-forme ou omettre les octets de remplissage implicites, utilisez la commande standard la taille et l'alignement au lieu de native taille et alignement : voir Ordre, taille et alignement des octets pour les détails.

Pour supprimer l'octet d'alignement et justifier vos hypothèses sur la position des octets tout en conservant l'ordre natif des octets, utilisez

struct.pack('=HcHH', port, flags, len(fragments), len(name))

Vous pouvez également utiliser un ordre d'octet fixe en utilisant la commande < o > comme préfixe.

La solution "correcte" consiste à utiliser unpack pour récupérer vos chiffres, de sorte que vous n'avez pas à vous soucier de l'endianness, du remplissage ou de quoi que ce soit d'autre, vraiment.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X