converter big endian para little endian em C

Eu preciso escrever uma function para converter big endian para little endian em C. Eu não posso usar qualquer function de biblioteca.

    Supondo que você precisa de uma simples troca de bytes, tente algo como

    Conversão não assinada de 16 bits:

    swapped = (num>>8) | (num< <8); 

    Conversão de 32 bits sem assinatura:

     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 

    Isso troca os pedidos de byte das posições 1234 a 4321. Se a sua input for 0xdeadbeef , uma swap endian de 32 bits poderá ter uma saída de 0xefbeadde .

    O código acima deve ser limpo com macros ou pelo menos constantes em vez de números mágicos, mas espero que ajude como é

    EDIT: como outra resposta apontou, existem alternativas específicas de plataforma, OS e conjunto de instruções que podem ser MUITO mais rápidas do que o acima. No kernel do Linux existem macros (cpu_to_be32 por exemplo) que lidam com o endianness muito bem. Mas essas alternativas são específicas para seus ambientes. Na prática, o endianness é melhor tratado usando uma mistura de abordagens disponíveis

    Incluindo:

     #include  

    você pode obter uma versão otimizada das funções de troca de bytes dependentes da máquina. Então, você pode facilmente usar as seguintes funções:

     __bswap_32 (uint32_t input) 

    ou

     __bswap_16 (uint16_t input) 
     #include  //! 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); } 

    Atualização : Adicionado troca de bytes de 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); } 

    Aqui está uma versão bastante genérica; Eu não compilei, então provavelmente há erros de digitação, mas você deve ter a ideia,

     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: Isso não é otimizado para velocidade ou espaço. Destina-se a ser claro (fácil de depurar) e portátil.

    Atualização 2018-04-04 Adicionado o assert () para capturar o caso inválido de n == 0, conforme identificado pelo commenter @chux.

    Se você precisar de macros (por exemplo, sistema embarcado):

     #define SWAP_UINT16(x) (((x) >> 8) | ((x) < < 8)) #define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) < < 8) | ((x) << 24)) 

    Edit: Estas são funções de biblioteca. Segui-los é a maneira manual de fazê-lo.

    Estou absolutamente chocado com o número de pessoas que desconhecem __byteswap_ushort, __byteswap_ulong e __byteswap_uint64 . Claro que eles são específicos do Visual C ++, mas eles compilam até alguns códigos deliciosos em arquiteturas x86 / IA-64. 🙂

    Aqui está um uso explícito da instrução bswap , extraída desta página . Note que o formulário intrínseco acima será sempre mais rápido do que isso , eu só adicionei para dar uma resposta sem uma rotina de biblioteca.

     uint32 cq_ntohl(uint32 a) { __asm{ mov eax, a; bswap eax; } } 

    Como uma piada:

     #include  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; } 

    Isso funcionará / será mais rápido?

      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]; 

    aqui está uma maneira de usar o pshufb de instrução SSSE3 usando sua Intel intrínseca, supondo que você tenha um múltiplo 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; } 

    Aqui está uma function que tenho usado – testado e funciona em qualquer tipo de dados básico:

     // 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; } } 

    EDIT: esta function somente troca o endianness de palavras de 16 bits alinhadas. Uma function frequentemente necessária para as codificações UTF-16 / UCS-2. EDIT END.

    Se você quiser mudar o destino de um bloco de memory, você pode usar minha abordagem incrivelmente rápida. Sua matriz de memory deve ter um tamanho que seja múltiplo de 8.

     #include  #include  #include  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); } 

    Esse tipo de function é útil para alterar a falta de arquivos Unicode UCS-2 / UTF-16.

    Este trecho de código pode converter um número pequeno de Endbit de 32 bits em um número Big Endian.

     #include  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); } 

    Se você estiver executando em um processador x86 ou x86_64, o big endian é nativo. assim

    para valores de 16 bits

     unsigned short wBigE = value; unsigned short wLittleE = ((wBigE & 0xFF) < < 8) | (wBigE >> 8); 

    para valores de 32 bits

     unsigned int iBigE = value; unsigned int iLittleE = ((iBigE & 0xFF) < < 24) | ((iBigE & 0xFF00) << 8) | ((iBigE >> 8) & 0xFF00) | (iBigE >> 24); 

    Essa não é a solução mais eficiente, a menos que o compilador reconheça que isso é manipulação de nível de byte e gera código de troca de bytes. Mas isso não depende de nenhum truque de layout de memory e pode ser facilmente transformado em uma macro.