Pergunta

Estou trabalhando em uma implementação do protocolo Memcache, que, em alguns pontos, usa 64 bits valores inteiros. Esses valores devem ser armazenados em "Pedido de bytes de rede".

Eu gostaria que houvesse alguns uint64_t htonll(uint64_t value) Função para fazer a mudança, mas infelizmente, se existir, não consegui encontrá -la.

Então, eu tenho 1 ou 2 perguntas:

  • Existe algum portátil (Windows, Linux, AIX) Função padrão para fazer isso?
  • Se não houver essa função, como você a implementaria?

Tenho em mente uma implementação básica, mas não sei como verificar o Endianness em tempo de compilação para tornar o código portátil. Portanto, sua ajuda é mais do que bem -vinda aqui;)

Obrigada.


Aqui está a solução final que escrevi, graças à solução 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;
    }
}
Foi útil?

Solução

Você provavelmente está procurando bswap_64 Eu acho que é suportado praticamente em todos os lugares, mas eu não chamaria de padrão.

Você pode verificar facilmente o endianness criando um int com um valor de 1, lançando o endereço do seu INT como um char* e verificando o valor do primeiro byte.

Por exemplo:

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

Sabendo disso, você também pode fazer uma função simples que faça a troca.


Você também pode sempre usar o Boost, que contém macros Endian, que são plataformas cruzadas portáteis.

Outras dicas

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

O teste (1 == htonl (1)) simplesmente determina (no tempo de execução tristemente) se a arquitetura de hardware reticular bytes trocando. Não há maneiras portáteis de determinar em tempo de compilação qual é a arquitetura, então recorremos ao uso de "Htonl", que é tão portátil quanto nessa situação. Se for necessário trocar de byte, trocamos 32 bits por vez usando o Htonl (lembrando-se de trocar as duas palavras de 32 bits).


Aqui está outra maneira de executar a troca portátil na maioria dos compiladores e sistemas operacionais, incluindo AIX, BSDs, Linux e Solaris.

#if __BIG_ENDIAN__
# define htonll(x) (x)
# define ntohll(x) (x)
#else
# define htonll(x) ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
# define ntohll(x) ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
#endif

A parte importante é usar __BIG_ENDIAN__ ou __LITTLE_ENDIAN__; e não __BYTE_ORDER__, __ORDER_BIG_ENDIAN__ ou __ORDER_LITTLE_ENDIAN__. Alguns compiladores e sistemas operacionais não têm __BYTE_ORDER__ e amigos.

Você pode tentar com uint64_t htobe64(uint64_t host_64bits) & uint64_t be64toh(uint64_t big_endian_64bits) para vice-versa.

Isso parece funcionar em c; eu fiz algo errado?

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

Para reduzir a sobrecarga do "se num == ...", use o pré-processador define:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#else
#endif

Editar: Combinando os dois (Código de Brian usado):

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

Aviso: código não testado! Teste antes de usar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top