J'ai besoin d'écrire une fonction pour convertir le gros endian en petit endian en C. Je ne peux utiliser aucune fonction de bibliothèque.
En supposant que vous ayez besoin d’un simple échange d’octets, essayez quelque chose comme:
Conversion 16 bits non signée:
swapped = (num>>8) | (num<<8);
Conversion 32 bits non signée:
swapped = ((num>>24)&0xff) | // move byte 3 to byte 0
((num<<8)&0xff0000) | // move byte 1 to byte 2
((num>>8)&0xff00) | // move byte 2 to byte 1
((num<<24)&0xff000000); // byte 0 to byte 3
Ceci permute les ordres d'octet des positions 1234 aux positions 4321. Si votre entrée était 0xdeadbeef
, un échange endian 32 bits peut produire une sortie de 0xefbeadde
.
Le code ci-dessus devrait être nettoyé avec des macros ou au moins des constantes à la place des nombres magiques, mais j'espère
EDIT: comme une autre réponse l’a souligné, il existe des alternatives spécifiques à la plate-forme, au système d’exploitation et aux jeux d’instructions qui peuvent être BEAUCOUP plus rapides que celles décrites ci-dessus. Dans le noyau Linux, il y a des macros (cpu_to_be32 par exemple) qui gèrent très bien l'endianité. Mais ces alternatives sont spécifiques à leurs environnements. En pratique, il est préférable de gérer l'endianisme en utilisant une combinaison d'approches disponibles.
En incluant:
#include <byteswap.h>
vous pouvez obtenir une version optimisée des fonctions de permutation d'octets dépendant de la machine. Ensuite, vous pouvez facilement utiliser les fonctions suivantes:
__bswap_32 (uint32_t input)
ou
__bswap_16 (uint16_t input)
#include <stdint.h>
//! Byte swap unsigned short
uint16_t swap_uint16( uint16_t val )
{
return (val << 8) | (val >> 8 );
}
//! Byte swap short
int16_t swap_int16( int16_t val )
{
return (val << 8) | ((val >> 8) & 0xFF);
}
//! Byte swap unsigned int
uint32_t swap_uint32( uint32_t val )
{
val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF );
return (val << 16) | (val >> 16);
}
//! Byte swap int
int32_t swap_int32( int32_t val )
{
val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF );
return (val << 16) | ((val >> 16) & 0xFFFF);
}
Mise à jour: Ajout de la permutation d'octets 64 bits
int64_t swap_int64( int64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL);
}
uint64_t swap_uint64( uint64_t val )
{
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | (val >> 32);
}
Voici une version assez générique; Je ne l'ai pas compilé, donc il y a probablement des fautes de frappe, mais vous devriez avoir l'idée,
void SwapBytes(void *pv, size_t n)
{
assert(n > 0);
char *p = pv;
size_t lo, hi;
for(lo=0, hi=n-1; hi>lo; lo++, hi--)
{
char tmp=p[lo];
p[lo] = p[hi];
p[hi] = tmp;
}
}
#define SWAP(x) SwapBytes(&x, sizeof(x));
NB: Ceci est non optimisé pour vitesse ou espace. Il se veut clair (facile à déboguer) et portable.
Mise à jour 2018-04-04 Ajout de l'assert () pour intercepter le cas invalide de n == 0, comme indiqué par commenter @chux.
Si vous avez besoin de macros (système intégré, par exemple):
#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
Edit: Ce sont des fonctions de bibliothèque. Les suivre est le moyen manuel de le faire.
Je suis absolument abasourdi par le nombre de personnes ignorant __ byteswap_ushort, __byteswap_ulong et __byteswap_uint64 . Bien sûr, ils sont spécifiques à Visual C++, mais ils compilent un code délicieux sur des architectures x86/IA-64. :)
Voici une utilisation explicite de l'instruction bswap
, extraite de cette page . Notez que la forme intrinsèque ci-dessus sera toujours toujours plus rapide que cela , je l'ai seulement ajouté pour donner une répondre sans routine de bibliothèque.
uint32 cq_ntohl(uint32 a) {
__asm{
mov eax, a;
bswap eax;
}
}
Comme une blague:
#include <stdio.h>
int main (int argc, char *argv[])
{
size_t sizeofInt = sizeof (int);
int i;
union
{
int x;
char c[sizeof (int)];
} original, swapped;
original.x = 0x12345678;
for (i = 0; i < sizeofInt; i++)
swapped.c[sizeofInt - i - 1] = original.c[i];
fprintf (stderr, "%x\n", swapped.x);
return 0;
}
voici un moyen d'utiliser l'instruction SSSE3 pshufb avec son code Intel intrinsèque, en supposant que vous ayez un multiple de 4 int
s:
unsigned int *bswap(unsigned int *destination, unsigned int *source, int length) {
int i;
__m128i mask = _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3);
for (i = 0; i < length; i += 4) {
_mm_storeu_si128((__m128i *)&destination[i],
_mm_shuffle_epi8(_mm_loadu_si128((__m128i *)&source[i]), mask));
}
return destination;
}
Cela fonctionnera-t-il/sera-t-il plus rapide?
uint32_t swapped, result;
((byte*)&swapped)[0] = ((byte*)&result)[3];
((byte*)&swapped)[1] = ((byte*)&result)[2];
((byte*)&swapped)[2] = ((byte*)&result)[1];
((byte*)&swapped)[3] = ((byte*)&result)[0];
Cet extrait de code peut convertir un petit nombre Endian de 32 bits en un nombre Big Endian.
#include <stdio.h>
main(){
unsigned int i = 0xfafbfcfd;
unsigned int j;
j= ((i&0xff000000)>>24)| ((i&0xff0000)>>8) | ((i&0xff00)<<8) | ((i&0xff)<<24);
printf("unsigned int j = %x\n ", j);
}
EDIT: Cette fonction permute uniquement l’endianité des mots 16 bits alignés. Une fonction souvent nécessaire pour les codages UTF-16/UCS-2. EDIT END.
Si vous voulez changer l’endurance d’un bloc de mémoire, vous pouvez utiliser mon approche extrêmement rapide. Votre matrice de mémoire doit avoir une taille multiple de 8.
#include <stddef.h>
#include <limits.h>
#include <stdint.h>
void ChangeMemEndianness(uint64_t *mem, size_t size)
{
uint64_t m1 = 0xFF00FF00FF00FF00ULL, m2 = m1 >> CHAR_BIT;
size = (size + (sizeof (uint64_t) - 1)) / sizeof (uint64_t);
for(; size; size--, mem++)
*mem = ((*mem & m1) >> CHAR_BIT) | ((*mem & m2) << CHAR_BIT);
}
Ce type de fonction est utile pour changer l’endurance des fichiers Unicode UCS-2/UTF-16.
Voici une fonction que j'ai utilisée - testée et qui fonctionne avec n'importe quel type de données de base:
// SwapBytes.h
//
// Function to perform in-place endian conversion of basic types
//
// Usage:
//
// double d;
// SwapBytes(&d, sizeof(d));
//
inline void SwapBytes(void *source, int size)
{
typedef unsigned char TwoBytes[2];
typedef unsigned char FourBytes[4];
typedef unsigned char EightBytes[8];
unsigned char temp;
if(size == 2)
{
TwoBytes *src = (TwoBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[1];
(*src)[1] = temp;
return;
}
if(size == 4)
{
FourBytes *src = (FourBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[3];
(*src)[3] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[2];
(*src)[2] = temp;
return;
}
if(size == 8)
{
EightBytes *src = (EightBytes *)source;
temp = (*src)[0];
(*src)[0] = (*src)[7];
(*src)[7] = temp;
temp = (*src)[1];
(*src)[1] = (*src)[6];
(*src)[6] = temp;
temp = (*src)[2];
(*src)[2] = (*src)[5];
(*src)[5] = temp;
temp = (*src)[3];
(*src)[3] = (*src)[4];
(*src)[4] = temp;
return;
}
}