2009-04-07 10 views
7

Necesito convertir una estructura de campo de bits de arquitectura little-endian a big-endia. ¿Cuál es la mejor manera de hacerlo, ya que habrá problemas en los límites de bytes, si simplemente intercambio los elementos de la estructura.Convirtiendo Endianess en una estructura de campo de bit

Estructura ex es:

struct { 
    unsigned int b1:1; 
    unsigned int b2:8; 
    unsigned int b3:7; 
    unsigned int b4:8; 
    unsigned int b5:7; 
    unsigned int b6:1; 
}; 
+0

Tu pregunta fue suficiente para responder mi pregunta sobre algo separado, ¡gracias! :) – Cyrus

Respuesta

-3

Para conseguir esto, finalmente obtuve una solución (algo derivado de la solución de epatel anterior). Esto es si convierto desde x86 a Solaris SPARC.

Tenemos que intercambiar primero la estructura entrante y luego leer los elementos en orden inverso. Básicamente, después de observar cómo se alinean las estructuras, vi que la endianess cambiaba tanto en el orden de los bytes como en el orden de los bits. Aquí hay un pseudo código.

struct orig 
{  
    unsigned int b1:1; 
    unsigned int b2:8; 
    unsigned int b3:7; 
    unsigned int b4:8; 
    unsigned int b5:7; 
    unsigned int b6:1; 
}; 

struct temp 
{  
    unsigned int b6:1; 
    unsigned int b5:7; 
    unsigned int b4:8; 
    unsigned int b3:7; 
    unsigned int b2:8; 
    unsigned int b1:1; 
}temp; 


func (struct orig *toconvert) 
{ 
    struct temp temp_val; 
    //Swap the bytes 
    swap32byte((u32*)toconvert); 
    //Now read the structure in reverse order - bytes have been swapped 
    (u32*)&temp_val = (u32 *)toconvert; 
    //Write it back to orignal structure 
    toconvert->b6=temp_val.b6; 
    toconvert->b5=temp_val.b5; 
    toconvert->b4=temp_val.b4; 
    toconvert->b3=temp_val.b3; 
    toconvert->b2=temp_val.b2; 
    toconvert->b1=temp_val.b1; 

}

Después de algunos experimentos he encontrado que este enfoque sólo es válido si los elementos llenan completamente la estructura, es decir, no hay bits no utilizados.

+0

Estoy seguro de que esto es una votación negativa porque es una mala idea, pero aterricé en esta página porque no estoy familiarizado con los campos de bits y no sé por qué es una mala idea. ¿Podría alguien explicar por favor? – Bear

1

usted tiene dos secciones de 16 bits allí (los tres primeros campos y los tres últimos campos son de 16 bits).

Eso es sólo 65536 entradas. Entonces, tenga una tabla de búsqueda que contenga la versión invertida de bit de los campos. Ajustar la estructura en una unión con otra estructura que tiene dos campos de 16 bits para hacer esto más fácil?

Algo así (no probado, no estoy cerca de un compilador de C):

union u { 
    struct { 
     unsigned int b1:1; 
     unsigned int b2:8; 
     unsigned int b3:7; 
     unsigned int b4:8; 
     unsigned int b5:7; 
     unsigned int b6:1; 
    } bits; 
    struct { 
     uint16 first; 
     uint16 second; 
    } words 
} ; 

unit16 lookup[65536]; 

/* swap architectures */ 

void swapbits (union u *p) 
{ 
    p->words.first = lookup[p->words.first]; 
    p->words.second = lookup[p->words.second]; 
} 

Población de la tabla de búsqueda deja como ejercicio para el lector :)

Sin embargo, leer el documento compilador cuidadosamente. No estoy seguro si el estándar C requiere que struct se ajuste a una palabra (aunque espero que la mayoría de los compiladores lo hagan).

+2

A menos que el rendimiento sea absolutamente crítico, este código desperdicia 128k de memoria. No es de extrañar que 4Gb ya no se consideren suficientes para un trabajo productivo ;-) – MaxVT

+0

Bueno, tal vez es fundamental, no nos han dicho. –

8

Puede usar un entero de 32 bits y extraer información de él utilizando los operadores y- y bitshift. Con eso en su lugar, simplemente podría usar htonl (host-to-network, long). El orden de bytes de red es big endian.

Esto no será tan elegante como un campo de bits, pero al menos sabrá lo que tiene y no tendrá que preocuparse por el compilador de sus estructuras.

+0

+1 Para mí, htonl() o htons() combinado con máscaras de bits y cambios de bit es el enfoque más sostenible para este tipo de cosas. – mouviciel

+0

Sí, usted está en lo correcto, aunque el método de epatel como el que se muestra a continuación también funciona, solo necesito ver dónde no funcionará :) – foo

+0

El método dado por epatel es muy común también (y también lo voté). Pero puede ser complicado cuando los campos de bits se superponen a un límite de bytes. – mouviciel

5

En un código de portado de proyecto de MIPS a Linux/x86 nos gustó esto.

struct { 

#ifdef __ONE_ENDIANESS__ 
    unsigned int b1:1; 
    unsigned int b2:8; 
    unsigned int b3:7; 
    unsigned int b4:8; 
    unsigned int b5:7; 
    unsigned int b6:1; 
#define _STRUCT_FILLED 
#endif /* __ONE_ENDIANESS__ */ 

#ifdef __OTHER_ENDIANESS__ 
    unsigned int b6:1; 
    unsigned int b5:7; 
    unsigned int b4:8; 
    unsigned int b3:7; 
    unsigned int b2:8; 
    unsigned int b1:1; 
#define _STRUCT_FILLED 
#endif /* __OTHER_ENDIANESS__ */ 

}; 

#ifndef _STRUCT_FILLED 
# error Endianess uncertain for struct 
#else 
# undef _STRUCT_FILLED 
#endif /* _STRUCT_FILLED */ 

Las macros __ONE_ENDIANESS__ y __OTHER_ENDIANESS__ fue el apropiado para el compilador usado hasta puede que tenga que mirar en que es apropiado para usted ...

+1

Observe que los campos b2 y b5 abarcan más de un byte en el primer ejemplo, por lo que no es probable que se puedan volver a escribir para que coincidan en el segundo caso. De lo contrario, este truco puede ahorrar una gran cantidad de cabello tirando. – RBerteig

+0

¿No? Creo que si sizeof (int) funcionó bien para nosotros ... – epatel

+0

ah;) vi ahora ... Estaba editando sin mirar ... ¡arreglando! – epatel

0

debería ser suficiente para intercambiar los bytes. La posición de bit dentro de un byte es la misma en big y little endian.
p. :

char* dest = (char*)&yourstruct; 
unsigned int orig = yourstruct; 
char* origbytes = (char*)&orig; 
dest[0] = origbytes[3]; 
dest[1] = origbytes[2]; 
dest[2] = origbytes[1]; 
dest[3] = origbytes[0]; 
+0

Por lo que puedo decir, el estándar ANSI C no especifica el orden en que los campos de bits se asignan dentro de un byte (o palabra), por lo que el intercambio de bytes puede no ser suficiente. –

+0

sí, no será portátil. pero supongo * que la mayoría de los compiladores deberían poner los bits en el lugar "natural" (por ejemplo, struct {unsigned char a: 1, b: 6, c: 1} ---> un bit 0, b bit 1-6, c bit 7) ... si la portabilidad está en primer lugar, use el consejo de huevas. – qwerty

1

Quiere hacer esto entre el canal (archivo o red) y su estructura. Mi práctica preferida es aislar las E/S de archivos de las estructuras mediante el código de escritura que construye los almacenamientos intermedios de archivos en una representación conocida y el código de lectura correspondiente que invierte esa transformación.

Su ejemplo específico es particularmente difícil de adivinar porque los campos de bits están definidos como unsigned int y sizeof(unsigned int) es particularmente no portátil.

Suponiendo que SWAG sea sizeof(int)==4, obtener un puntero a la estructura y reescribir los bytes individuales probablemente le proporcione la respuesta que desea.

El truco de definir la estructura de forma diferente para diferentes plataformas fuerza de trabajo, pero en el ejemplo que citas no hay una rotura limpia en los límites de bytes, por lo que no es probable que sea posible producir un equivalente de una plataforma en el otro sin dividir uno o más de los campos en dos partes.

0

No debe usar campos de bits cuando el diseño físico es importante porque está definido por la implementación en qué orden se llena la palabra más grande.

+0

a menudo encabezados de paquetes (donde el diseño físico es extremadamente importante) use campos de bits para definir campos de sub-bytes. Tomemos como ejemplo el encabezado IP del kernel de Linux en netinet/ip.h (o http://lxr.linux.no/linux+v2.6.38/include/linux/ip.h#L80) – jbenet

+0

Um, ¿y qué? Están codificando un compilador específico (gcc). – zvrba

+0

Esto no responde la pregunta del OP; el OP habrá tenido esto en cuenta. – Qix

6

Endurecimiento del procesador no está relacionado con el orden del campo de bits. Es muy posible tener dos compiladores en la misma computadora que usen pedidos opuestos para bitfields. Por lo tanto, teniendo en cuenta esto:

union { 
    unsigned char x; 
    struct { 
     unsigned char b1 : 1; 
     unsigned char b2 : 7; 
    }; 
} abc; 
abc.x = 0; 
abc.b1 = 1; 
printf("%02x\n", abc.x); 

A menos que le sucede que tiene una documentación detallada, la única manera de saber si ese imprimirá 01 o 80 es probarlo.

Cuestiones relacionadas