[center]Bonjour,[/center]

je suis amené ces temps-ci à relire des fichiers binaires enregistrés au format big-endian, ma distribution F14 tournant sur une plateforme Intel, donc little-endian, j'utilise Ghex pour m'aider à déboguer mon programme, mais comme vous allez le constater, j'ai rencontré quelques problèmes

à titre d'information, la version de ghex utilisée correspond au rpm suivant : ghex-2.24.0-5.fc13.x86_64

les données binaires à relire sont enregistrées dans des fichiers générés par LabVIEW, et contiennent des données au format cluster (un équivalent du struct C/C++), avec dans ce cluster, un tableau de nombres réels au format double (donc codé sur 64 bits)

à l'aide de LabVIEW, j'ai regénéré le fichier, à l'origine au format big-endian, pour l'enregistrer au format little-endian

dans ce format little-endian, voilà ce que Ghex me donne (voir première image ci-dessous)
  • les 4 premiers octets (position 0x0=0) contiennent un nombre entier signé sur 32 bits (signed int) : c'est un numéro d'ordre, ici égal à 1
    • 00 00 00 01 en little-endian donne donc le nombre 1
  • les 4 octets suivants (position 0x4=4) contiennent un entier signé de 32 bits (signed int) : c'est un numéro d'ordre, ici égal à 21468
    • 00 00 53 DC en little-endian donne donc le nombre 21468
  • les 4 octets suivants (position 0x8=8) contiennent un entier signé de 32 bits (signed int) : c'est le nombre de lignes du tableau de double, ici égal à 100
    • 00 00 00 64 en little-endian donne donc le nombre 100
  • les 4 octets suivants (position 0xC=12) contiennent un entier signé de 32 bits (signed int) : c'est le nombre de colonnes du tableau de double, ici égal à 1
    • 00 00 00 01 en little-endian donne donc le nombre 1
on arrive ainsi au premier élément au format double à lire ; sa valeur est environ égale à : 1535.383
  • les 8 octets suivants (position 0x10=16) contiennent le nombre au format double, donc 64 bits : c'est le premier nombre du tableau
    • 40 97 FD 88 04 71 A2 C4 en little-endian donne donc le nombre 1535.383
lecture du nombre au format flottant 64 bits _type C double_ enregistré au format little-endian


la lecture se passe bien quant on relit le fichier au format little-endian, mais ça déraille quand on prend le fichier contenant les mêmes données, mais au format big-endian ; en effet, au lieu de 1535.383, on obtient 2,895473e-287 (voir image ci-dessous, avec l'option little-endian désactivée puisque les données contenues dans le fichier sont au format big-endian)

au format big-endian (et je l'ai vérifié par ailleurs avec un petit bout de programme C++, voir plus bas), le nombre signifie pourtant bien 1535.383
  • nombre au format little endian
    • 40 97 FD 88 04 71 A2 C4
  • même nombre au format big endian
    • C4 A2 71 04 88 FD 97 40
et c'est de même pour les 99 nombres au format double suivants dont la relecture par ce moyen est tout aussi incohérente (les valeurs affichées en flottant 32 bits _type C float_ par Ghex sont quant à elles bien correctes ... seule la lecture du format flottant 64 bits _type C double_ semble erronée)

lecture erronée du nombre au format flottant 64 bits _type C double_ enregistré au format big-endian


Ghex m'a induit en erreur pendant pas mal de temps, c'est alors que j'ai remarqué qu'un décalage de 4 octets vers la gauche du pointeur de lecture de Ghex permettait alors d'afficher les nombres (situés en réalité 4 octets plus loin) au format flottant 64 bits de façon correcte

lecture "correcte" du nombre au format flottant 64 bits _type C double_ enregistré au format big-endian, seulement si un décalage de 4 octets à gauche est effectué


là aussi, si un décalage de 4 octets est effectué sur les 99 nombres flottants 64 bits suivants, on parvient à voir les nombres correctement, mais le problème est qu'on ne pointe pas à la bonne place, d'où très probablement un bug lors de la lecture de flottants 64 bits dans des fichiers enregistrés au format big-endian

êtes-vous d'accord avec ma conclusion ?

si oui, y-aurait-il moyen de signaler ce bug quelque part pour qu'il puisse être traité ?

d'avance merci

en complément, un petit programme qui permet de tester en big-endian et little-endian, la lecture du nombre flottant décrit ci-dessus
#include <iostream>
#include <cstdlib>

// ---------------------------------------------------
// convertit little-endian en big-endian et vice-versa
// ---------------------------------------------------

void
endian_swap_64(int64_t& x)
{
#ifdef __GNUC__
  x = __builtin_bswap64(x);
#else
  x = ((x<<56) & 0xFF00000000000000) |        \
    ((x<<40) & 0x00FF000000000000) |        \
    ((x<<24) & 0x0000FF0000000000) |        \
    ((x<<8)  & 0x000000FF00000000) |        \
    ((x>>8)  & 0x00000000FF000000) |        \
    ((x>>24) & 0x0000000000FF0000) |        \
    ((x>>40) & 0x000000000000FF00) |        \
    ((x>>56) & 0x00000000000000FF);
#endif
}

// ---------------------------------------------------
// lit un mot de 64 bits en le mettant au bon format
// ---------------------------------------------------

unsigned char*
lecture_64(const bool little_endian, unsigned char *const ptr_chaine)
{
#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
  if (!little_endian) {
    endian_swap_64(*reinterpret_cast<int64_t*>(ptr_chaine));
  };
#else
  if (little_endian) {
    endian_swap_64(*reinterpret_cast<int64_t*>(ptr_chaine));
  };
#endif
  return (ptr_chaine);
}

// routine principale

int
main(void)
{
  unsigned char chaine[8];

  chaine[0] = 0xC4;
  chaine[1] = 0xA2;
  chaine[2] = 0x71;
  chaine[3] = 0x04;
  chaine[4] = 0x88;
  chaine[5] = 0xFD;
  chaine[6] = 0x97;
  chaine[7] = 0x40;

  std::cout<<"lecture au format little-endian : "<<*reinterpret_cast<double*>(lecture_64(true,chaine))<<std::endl;

  chaine[0] = 0x40;
  chaine[1] = 0x97;
  chaine[2] = 0xFD;
  chaine[3] = 0x88;
  chaine[4] = 0x04;
  chaine[5] = 0x71;
  chaine[6] = 0xA2;
  chaine[7] = 0xC4;

   std::cout<<"lecture au format big-endian : "<<*reinterpret_cast<double*>(lecture_64(false,chaine))<<std::endl;

   return (EXIT_SUCCESS);
}
Bonsoir,

La marche à suivre serait plutôt de contacter le ou les auteurs de ghex. Éventuellement avant essayer avec les toutes dernières versions depuis le dépôt git du projet (si ya du neuf par rapport avec la version qu'on a dans Fedora, mais ça a pas l'air de bouger des masses).

http://live.gnome.org/Ghex la page du projet. Il faudra surement utiliser https://bugzilla.gnome.org/ pour remonter le bug s'il persiste dans les versions plus récentes (voir avant s'il n'est pas déjà référencé).
OK, merci pour la réponse

apparemment, il y a eu de l'activité sur ce logiciel au moins jusqu'en 2009
Donc il n'a pas bougé depuis presque 2 ans, c'est que soit il a atteint la perfection totale (j'ai comme un doute), soit le projet est un peu mort.
Et avec d'autres logiciels je suppose que le problème n’apparaît pas? genre avec hexedit ou shed?
[center]Bonjour,
[/center]
je ne connais pas les deux outils cités (hexedit et shed), je vais regarder pour voir s'ils sont aussi commodes que Ghex, mais ce n'est pas gagné 🙁

au besoin, le fichier à problème (au format big-endian) se trouve ici (j'ai donc remonté le bug hier soir, après avoir jeté un coup d'oeil aux bugs précedemment cités) : fichier 'big' de test au format big-endian (ces mêmes données au format little-endian sont ici : fichier 'little' au format little-endian)

sous forme très simplifiée, le programme LabVIEW qui m'a permis de regénérer le fichier au format little-endian et à partir duquel j'ai eu la conviction d'un bug lors de la lecture dans l'autre format (l'original étant au format big-endian) est décrit dans l'image ci-dessous (pour faire simple, je n'ai extrait qu'un seul bloc de données dans le fichier 'big' du lien précédent)



concernant les variables présentes dans le cluster, outre un numéro d'ordre non présent dans la structure présente sur l'image (rajouté par LabVIEW : les 4 premiers octets du fichier), il y a en fin de cluster une structure de date codée sur 2 fois 64 bits, et stockée ici sous forme de tableau 1D, qui peut se décrire comme une structure

cette structure est décrite ici : http://zone.ni.com/reference/en-XX/help/370051T-01/cvi/libref/ni-btf/ ; elle est rappelée / reformulée ci-dessous :

struture de la variable temps utilisée dans le cluster LabVIEW ci-dessus
typedef struct {
    uint64_t lsb; // positive fractions (2⁻⁶⁴) of a second
    int64_t  msb; // number of whole seconds after 12:00 a.m., Friday, January 1, 1904, Universal Time
  } CVITime;
concernant les tableaux LabVIEW, ils ont leur équivalent C/C++ que je précise ci-dessous (j'ai ré-écrit en style C++ avec un template, c'est beaucoup plus modulaire quand on a des tableaux de plusieurs types à gérer)

format équivalent C++ des tableaux 1D LabVIEW
template<class T>
  struct TD1 {
    int32_t dimSize;
    T elt[1];
  };
format équivalent C++ des tableaux 2D LabVIEW
template<class T>
  struct TD2 {
    int32_t dimSizes[2];
    T elt[1];
  };
les types int32_t, uint64_t et int64_t sont définis quant à eux dans le fichier /usr/include/stdint.h

toutes les données sont mises à plat dans le fichier binaire (donc pas de problème d'alignement de données)

le problème survient donc lors de la lecture du tableau 2D, second élément du cluster (100 lignes et 1 colonne dans cet exemple) contenant des nombres flottants codés sur 64 bits (type C double), ces valeurs étant toutes situées autour de 1535 (c'est une valeur de longueur d'onde exprimée en nanomètres fournie par un appareil de mesure)

il est probable que ce même problème soit présent lors de la lecture des données contenues dans le tableau temps 1D (dernier élément du cluster), mais je n'ai pas vérifié

pour info, le programme de décodage C++ que j'ai eu du mal à mettre au point (Ghex m'ayant induit en erreur un bon bout de temps ...) arrive désormais à décoder correctement toutes ces valeurs, d'où ma conviction d'un bug ...
4 mois plus tard