Lecture de données binaires compressées sur 32 bits sur un système 64 bits

StackOverflow https://stackoverflow.com/questions/135246

  •  02-07-2019
  •  | 
  •  

Question

J'essaie d'écrire une extension Python C qui lit des données binaires condensées (elles sont stockées sous la forme de structures de structures), puis les analyse dans des objets Python. Tout fonctionne comme prévu sur une machine 32 bits (les fichiers binaires sont toujours écrits sur une architecture 32 bits), mais pas sur une boîte de 64 bits. Existe-t-il un & Quot; préféré & Quot; façon de faire cela?

Ce serait beaucoup de code à poster mais à titre d'exemple:

struct
{
    WORD    version;
    BOOL    upgrade;
    time_t  time1;
            time_t  time2;
} apparms;

File *fp;
fp = fopen(filePath, "r+b");
fread(&apparms, sizeof(apparms), 1, fp);
return Py_BuildValue("{s:i,s:l,s:l}",
  "sysVersion",apparms.version,
  "powerFailTime", apparms.time1,
  "normKitExpDate", apparms.time2
 );

Maintenant, sur un système 32 bits, cela fonctionne très bien, mais sur un 64 bits, mes tailles time_t sont différentes (longueurs 32 bits / 64 bits).

Zut, vous êtes rapides.

Patrick, j’ai commencé à utiliser le paquetage struct mais j’ai trouvé le moyen de ralentir pour répondre à mes besoins. De plus, je cherchais une excuse pour écrire une extension Python.

Je sais que c'est une question stupide, mais de quels types dois-je faire attention?

Merci.

Était-ce utile?

La solution

Spécifiez explicitement que vos types de données (par exemple, des entiers) sont 32 bits. Sinon, si vous avez deux nombres entiers côte à côte, vous les lirez comme un entier de 64 bits.

Lorsque vous traitez avec des problèmes multi-plateformes, les deux principaux points à surveiller sont les suivants:

  1. Bitness. Si vos données condensées sont écrites avec des ints 32 bits, alors tout votre code doit spécifier explicitement des ints 32 bits lors de la lecture des écritures et .
  2. ordre des octets. Si vous déplacez votre code des puces Intel vers PPC ou SPARC, votre ordre d'octet sera incorrect. Vous devrez importer vos données, puis basculer octet pour les faire correspondre à l'architecture actuelle. Sinon, 12 (0x0000000C) sera lu comme 201326592 (0x0C000000).

Espérons que cela aide.

Autres conseils

Le module 'struct' devrait pouvoir le faire, bien que l'alignement des structures au milieu des données pose toujours un problème. Cependant, il n’est pas très difficile de faire les choses correctement: recherchez (une fois) la limite à laquelle les structures de structure sont alignées, puis complétez (manuellement, avec le spécificateur 'x') à cette limite. Vous pouvez vérifier votre remplissage en comparant struct.calcsize () avec vos données réelles. C’est certainement plus facile que d’écrire une extension C pour cela.

Pour continuer à utiliser Py_BuildValue () de cette manière, vous avez deux options. Vous pouvez déterminer la taille de time_t au moment de compiletime (en termes de types fondamentaux, donc 'un int' ou 'un long' ou 'un ssize_t'), puis utiliser le bon caractère de format pour Py_BuildValue - 'i' pour un int, 'l' pendant longtemps, 'n' pour un ssize_t. Ou vous pouvez utiliser PyInt_FromSsize_t () manuellement, auquel cas le compilateur effectue la conversion en amont pour vous, puis utilise les caractères de format "O" pour transmettre le résultat à Py_BuildValue.

Vous devez vous assurer que vous utilisez des membres indépendants de l'architecture pour votre structure. Par exemple, un int peut être 32 bits sur une architecture et 64 bits sur une autre. Comme d'autres l'ont suggéré, utilisez plutôt les types de style int32_t. Si votre structure contient des membres non alignés, vous devrez peut-être également traiter le remplissage ajouté par le compilateur.

Un autre problème courant avec les données inter-architectures est l’endianisme. L’architecture Intel i386 est peu évolutive, mais si vous lisez sur une machine complètement différente (une Alpha ou Sparc, par exemple), vous devrez également vous en préoccuper.

Le module struct Python traite ces deux situations, en utilisant le préfixe transmis dans la chaîne de format.

  • @ - Utilisez une taille, une finalité et un alignement natifs. i = sizeof (int), l = sizeof (long)
  • = - Utilisez l’endianité native, mais les tailles et l’alignement standard (i = 32 bits, l = 64 bits)
  • < - tailles standard / alignement little-endian
  •   
        
    • Tailles / alignements standard big-endian
    •   

En général, si les données sont transmises par votre machine, vous devez indiquer le format et le format de la taille / remplissage à quelque chose de spécifique & # 8212; c'est à dire. utilisez " < " ou " > " comme votre format. Si vous souhaitez gérer cela dans votre extension C, vous devrez peut-être ajouter du code pour le gérer.

Quel est votre code pour lire les données binaires? Assurez-vous de copier les données dans des types correctement dimensionnés, tels que int32_t au lieu de int.

Pourquoi n'utilisez-vous pas le package struct ?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top