Preciso escrever uma função para converter big endian em little endian em C. Não consigo usar nenhuma função de biblioteca.
c
swap
endianness
Alex Xander
fonte
fonte
Respostas:
Supondo que você precisa de uma simples troca de bytes, tente algo como
Conversão de 16 bits sem sinal:
Conversão de 32 bits sem sinal:
Isso troca as ordens de bytes das posições 1234 para 4321. Se a sua entrada foi
0xdeadbeef
, um swap endian de 32 bits pode ter a saída de0xefbeadde
.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 está
EDITAR: como outra resposta apontou, existem alternativas específicas de plataforma, sistema operacional e conjunto de instruções que podem ser MUITO mais rápidas do que as anteriores. No kernel do Linux existem macros (cpu_to_be32 por exemplo) que lidam muito bem com o endianness. Mas essas alternativas são específicas para seus ambientes. Na prática, o endianismo é melhor tratado usando uma combinação de abordagens disponíveis
fonte
((num & 0xff) >> 8) | (num << 8)
, o gcc 4.8.3 gera uma únicarol
instrução. E se a conversão de 32 bits for escrita como((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24)
, o mesmo compilador gerará uma únicabswap
instrução.struct byte_t reverse(struct byte_t b) { struct byte_t rev; rev.ba = b.bh; rev.bb = b.bg; rev.bc = b.bf; rev.bd = b.be; rev.be = b.bd; rev.bf = b.bc; rev.bg = b.bb; rev.bh = b.ba; return rev;}
por campos de bits como este: onde este é um campo de bits com 8 campos de 1 bit cada. Mas não tenho certeza se isso é tão rápido quanto as outras sugestões. Para ints, useunion { int i; byte_t[sizeof(int)]; }
para inverter byte a byte no inteiro.Incluindo:
você pode obter uma versão otimizada das funções de troca de bytes dependentes da máquina. Então, você pode usar facilmente as seguintes funções:
ou
fonte
#include <byteswap.h>
, veja o comentário no próprio arquivo .h. Esta postagem contém informações úteis, então votei a favor, apesar de o autor ignorar o requisito do OP de não usar uma função lib.Atualização : Adicionada troca de bytes de 64 bits
fonte
int32_t
eint64_t
, qual é o raciocínio por trás do mascaramento de... & 0xFFFF
e... & 0xFFFFFFFFULL
? Há algo acontecendo com a extensão de sinal aqui que não estou vendo? Além disso, por que estáswap_int64
voltandouint64_t
? Não deveria ser assimint64_t
?swap_int64
em sua resposta. 1 para a resposta útil, BTW!LL
são desnecessários(u)swap_uint64()
bem como umL
não é necessário(u)swap_uint32()
. OU
não é necessáriouswap_uint64()
tanto quanto oU
não é necessário emuswap_uint32()
Aqui está uma versão bastante genérica; Eu não compilei, então provavelmente há erros de digitação, mas você deve ter uma ideia,
NB: Isso não éotimizado para velocidade ou espaço. Ele se destina a ser claro (fácil de depurar) e portátil.
Atualização 04-04-2018 Adicionado o assert () para capturar o caso inválido de n == 0, conforme observado pelo comentador @chux.
fonte
bswap
instrução por um compilador X86 decente com otimização habilitada. Esta versão com um parâmetro para o tamanho não poderia fazer isso.Se você precisar de macros (por exemplo, sistema incorporado):
fonte
UINT
em seu nome.Editar: são funções de biblioteca. Segui-los é a maneira manual de fazer isso.
Estou absolutamente surpreso 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 são compilados em alguns códigos deliciosos nas arquiteturas x86 / IA-64. :)
Aqui está um uso explícito da
bswap
instrução, extraída desta página . Observe que a forma intrínseca acima sempre será mais rápida do que isso , eu apenas a adicionei para dar uma resposta sem uma rotina de biblioteca.fonte
Como uma piada:
fonte
int i, size_t sizeofInt
e não do mesmo tipo para ambos.aqui está uma maneira de usar a instrução SSSE3 pshufb usando seu intrínseco Intel, supondo que você tenha um múltiplo de 4
int
s:fonte
Isso funcionará / será mais rápido?
fonte
char
, nãobyte
.Esta é uma função que tenho usado - testei e funciona em qualquer tipo de dados básico:
fonte
source
está alinhado conforme necessário - mas se essa suposição não for válida, o código é UB.EDIT: Esta função apenas troca o endianness de palavras alinhadas de 16 bits. Uma função freqüentemente necessária para codificações UTF-16 / UCS-2. EDIT END.
Se você quiser mudar a duração de um bloco de memória, pode usar minha abordagem incrivelmente rápida. Seu array de memória deve ter um tamanho múltiplo de 8.
Este tipo de função é útil para alterar o endianess de arquivos Unicode UCS-2 / UTF-16.
fonte
t know if it
tão rápido quanto as sugestões, mas funciona: github.com/heatblazer/helpers/blob/master/utils.hCHAR_BIT
em vez de8
é curioso, pois0xFF00FF00FF00FF00ULL
depende deCHAR_BIT == 8
. Observe queLL
não é necessário na constante.CHAR_BIT
para aumentar a exposição dessa macro. Quanto ao LL, é mais uma anotação do que qualquer outra coisa. Também é um hábito que peguei há muito tempo com compiladores buggy (pré-padrão) que não fariam a coisa certa.Este trecho de código pode converter um pequeno número Endian de 32 bits em um número Big Endian.
fonte
((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24);
pode ser mais rápido em algumas plataformas (por exemplo, reciclar as constantes da máscara AND). A maioria dos compiladores faria isso, mas alguns compiladores simples não são capazes de otimizá-lo para você.Se você estiver executando em um processador x86 ou x86_64, o big endian é nativo. tão
para valores de 16 bits
para valores de 32 bits
Essa não é a solução mais eficiente, a menos que o compilador reconheça que se trata de uma manipulação em nível de byte e gere o código de troca de bytes. Mas não depende de nenhum truque de layout de memória e pode ser transformado em uma macro com bastante facilidade.
fonte