2010-06-11 23 views
53

Estoy trabajando en una implementación del protocolo Memcache que, en algunos puntos, usa valores enteros de 64 bits. Estos valores deben almacenarse en "orden de bytes de red".¿Hay alguna función htonl "estándar" para enteros de 64 bits en C++?

Ojalá hubiera alguna función de uint64_t htonll(uint64_t value) para hacer el cambio, pero desafortunadamente, si existe, no pude encontrarlo.

Así que tengo 1 o 2 preguntas:

  • ¿Hay alguna portátil (Windows, Linux, AIX) función estándar de hacer esto?
  • Si no existe tal función, ¿cómo la implementaría?

Tengo en mente una implementación básica, pero no sé cómo comprobar el endianness en tiempo de compilación para hacer que el código sea portátil. Entonces su ayuda es más que bienvenida aquí;)

Gracias.


Aquí está la solución final que escribí, gracias a la solución de Brian.

uint64_t htonll(uint64_t value) 
{ 
    // The answer is 42 
    static const int num = 42; 

    // Check the endianness 
    if (*reinterpret_cast<const char*>(&num) == num) 
    { 
     const uint32_t high_part = htonl(static_cast<uint32_t>(value >> 32)); 
     const uint32_t low_part = htonl(static_cast<uint32_t>(value & 0xFFFFFFFFLL)); 

     return (static_cast<uint64_t>(low_part) << 32) | high_part; 
    } else 
    { 
     return value; 
    } 
} 
+2

posible duplicado de este http://stackoverflow.com/questions/809902/64-bit-ntohl-in-c – INS

+0

@ereOn: También tengo una pregunta similar [aquí] (http://stackoverflow.com/ preguntas/19393539/how-to-swap-64-bit-integer-while-extracting-bytes-from-bytearray-in-c). Si es posible, ¿puedes echar un vistazo y decirme qué mal estoy haciendo aquí? – AKIWEB

+1

En lugar de incluir su respuesta dentro de la pregunta, debe dejar su respuesta con respuestas. Es más legible – mpromonet

Respuesta

15

Usted probablemente está buscando bswap_64 creo que es compatible prácticamente en todas partes, pero yo no diría que es estándar.

Puede verificar fácilmente la endianidad creando un int con un valor de 1, convirtiendo la dirección de su int como char* y verificando el valor del primer byte.

Por ejemplo:

int num = 42; 
if(*(char *)&num == 42) 
{ 
    //Little Endian 
} 
else 
{ 
    //Big Endian 
} 

Sabiendo esto también se puede hacer una función simple que hace el intercambio.


También es posible usar siempre impulso que contiene macros endian que son multiplataforma portátil.

+0

Gracias;) Las macros 'boost' parecen interesantes. ¿Tienes un enlace? – ereOn

+1

Consulte http://www.boost.org/doc/libs/1_37_0/boost/detail/endian.hpp –

+3

Técnicamente, solo un byte-swap no siempre es suficiente. Hay sistemas que son middle-endian (aunque no * son * sistemas modernos). Además, 'bswap_64' definitivamente * NO * es compatible en todas partes; solo mire la larga lista de sistemas con la falta de 'bswap_64': https://www.gnu.org/software/gnulib/manual/html_node/bswap_005f64.html –

-3

EDIT: la combinación de los dos (código de Brian utilizado):

uint64_t htonll(uint64_t value) 
{ 
    int num = 42; 
    if(*(char *)&num == 42) 
      return (htonl(value & 0xFFFFFFFF) << 32LL) | htonl(value >> 32); 
    else 
      return value; 
} 

Advertencia: código no probado! Por favor prueba antes de usar.

+0

@Pavel Aún no funciona. 'htonl()' devuelve un valor de 32 bits en el que ** no puede ** llamar a '<< 32LL' (porque no puede desplazar 32 bits restantes en un valor de solo 32 bits). – ereOn

+0

Creo que cambiar por 32LL hará una promoción del lado izquierdo, ¿no? –

+0

@Pavel: no, el tamaño de bit del valor de cambio no cambia nada. gcc emite una advertencia: "contador de turno izquierdo> = ancho de tipo". Y su función dice que htonll (0x0102030405060708ULL) == 0xc070605. – bstpierre

4

Esto parece funcionar en C; ¿Hice algo mal?

uint64_t htonll(uint64_t value) { 
    int num = 42; 
    if (*(char *)&num == 42) { 
     uint32_t high_part = htonl((uint32_t)(value >> 32)); 
     uint32_t low_part = htonl((uint32_t)(value & 0xFFFFFFFFLL)); 
     return (((uint64_t)low_part) << 32) | high_part; 
    } else { 
     return value; 
    } 
} 
1

Para reducir la sobrecarga de la "si num == ..." Usa el pre-procesador define:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 
#else 
#endif 
12
#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) 
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) 

La prueba (1 htonl == (1)) simplemente determina (en tiempo de ejecución tristemente) si la arquitectura de hardware requiere el intercambio de bytes. No hay formas portátiles de determinar en tiempo de compilación qué es la arquitectura, por lo que recurrimos al uso de "htonl", que es lo más portátil posible en esta situación. Si se requiere el intercambio de bytes, intercambiamos 32 bits a la vez usando htonl (recordando intercambiar también las dos palabras de 32 bits).

+1

Estoy seguro de que esta respuesta es genial, pero ¿podría agregar algo más que el código para explicar qué y por qué? – AndyG

+0

¡Gracias! He editado tu respuesta con la explicación. – AndyG

+0

como se mencionó en @ereon, no se pueden usar cambios de 32 bits en 'ntohl' o' ntohl' porque devuelven 'uint32_t', por lo tanto, activan UB. – Aif

1

Usted puede tratar con uint64_t htobe64(uint64_t host_64bits) & uint64_t be64toh(uint64_t big_endian_64bits) para viceversa.

+1

Bueno, no, no puedo porque han pasado más de 6 años desde que toqué esa base de código;) – ereOn

Cuestiones relacionadas