c swap endianness

convertir big endian en little endian en C



swap endianness (13)

¿Esto funcionará / será más 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];

Necesito escribir una función para convertir big endian en little endian en C. No puedo usar ninguna función de biblioteca.


Aquí hay una función que he estado usando - probado y funciona en cualquier tipo de datos básicos:

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


Aquí hay una versión bastante genérica; No lo he compilado, por lo que probablemente haya errores tipográficos, pero debes tener la idea,

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: Esto no está optimizado para la velocidad o el espacio. Está destinado a ser claro (fácil de depurar) y portátil.

Actualización 2018-04-04 Se agregó assert () para atrapar el caso inválido de n == 0, como lo detectó el comentarista @chux.


Asumiendo que lo que necesita es un simple intercambio de bytes, intente algo como

Conversión de 16 bit sin firmar:

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

Conversión sin firmar de 32 bits:

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

Esto intercambia las órdenes de bytes desde las posiciones 1234 a 4321. Si su entrada fue 0xdeadbeef , un intercambio endian de 32 bits podría tener una salida de 0xefbeadde .

El código anterior debe limpiarse con macros o al menos constantes en lugar de números mágicos, pero es de esperar que ayude como es

EDITAR: como se señaló otra respuesta, hay plataforma, SO y opciones específicas del conjunto de instrucciones que pueden ser MUCHO más rápidas que las anteriores. En el kernel de Linux, hay macros (cpu_to_be32, por ejemplo) que manejan endianness muy bien. Pero estas alternativas son específicas de sus entornos. En la práctica, la endianidad se trata mejor usando una combinación de enfoques disponibles


Como una broma:

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


EDITAR: Esta función solo intercambia la endianidad de las palabras alineadas de 16 bits. Una función a menudo necesaria para las codificaciones UTF-16 / UCS-2. EDITAR FINALIZAR.

Si desea cambiar la endianess de un bloque de memoria puede usar mi enfoque increíblemente rápido. Su matriz de memoria debe tener un tamaño que sea un múltiplo 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); }

Este tipo de función es útil para cambiar la endiabilidad de los archivos Unicode UCS-2 / UTF-16.


Este fragmento de código puede convertir un pequeño número Endian de 32 bits en un número 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); }


Incluyendo:

#include <byteswap.h>

puede obtener una versión optimizada de las funciones de intercambio de bytes dependientes de la máquina. Luego, puede usar fácilmente las siguientes funciones:

__bswap_32 (uint32_t input)

o

__bswap_16 (uint16_t input)


Si está ejecutando un procesador x86 o x86_64, el big endian es nativo. asi que

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

Esta no es la solución más eficiente a menos que el compilador reconozca que esto es una manipulación de nivel de bytes y genera un código de intercambio de bytes. Pero no depende de ningún trucos de diseño de memoria y se puede convertir en una macro con bastante facilidad.


Si necesita macros (por ejemplo, sistema integrado):

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


esta es una forma de usar la instrucción SSSE3 pshufb usando su intrínseco Intel, suponiendo que tiene un 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; }


Editar: Estas son funciones de la biblioteca. Seguirlos es la forma manual de hacerlo.

Estoy absolutamente sorprendido por la cantidad de personas que no saben de __byteswap_ushort, __byteswap_ulong y __byteswap_uint64 . Claro que son específicos de Visual C ++, pero compilan hasta un delicioso código en las arquitecturas x86 / IA-64. :)

Aquí hay un uso explícito de la instrucción bswap , extraída de esta página . Tenga en cuenta que la forma intrínseca anterior siempre será más rápida que esta , solo la agregué para dar una respuesta sin una rutina de biblioteca.

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


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

Actualización : intercambio de bytes agregado 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); }