How to convert big endian to little endian in C without using library functions?
Asked Answered
S

15

117

I need to write a function to convert a big endian integer to a little endian integer in C. I cannot use any library function. How would I do this?

Shannonshanny answered 2/2, 2010 at 5:9 Comment(5)
a 16 bit value? 32 bit value? float? an array?Fallacy
time to choose an answer perhaps?Ducal
Voting to reopen. Same as #105752 for C++. We could just edit to make that clearer.Resa
I think it's clear enough. Please unclose the question.Janessa
gcc and g++ are properly detecting such swaps and convert them to one or two instructions, but you probably need to use -O3 or at least -O2. So you should write one simple function to do the swap with inline and it automatically will do the work for you.Carpo
P
204

Assuming what you need is a simple byte swap, try something like

Unsigned 16 bit conversion:

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

Unsigned 32-bit conversion:

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

This swaps the byte orders from positions 1234 to 4321. If your input was 0xdeadbeef, a 32-bit endian swap might have output of 0xefbeadde.

The code above should be cleaned up with macros or at least constants instead of magic numbers, but hopefully it helps as is

EDIT: as another answer pointed out, there are platform, OS, and instruction set specific alternatives which can be MUCH faster than the above. In the Linux kernel there are macros (cpu_to_be32 for example) which handle endianness pretty nicely. But these alternatives are specific to their environments. In practice endianness is best dealt with using a blend of available approaches

Phenanthrene answered 2/2, 2010 at 6:6 Comment(5)
+1 for mentioning platform/hardware-specific methods. Programs are always run on some hardware, and hardware features are always fastest.Luttrell
if the 16 bit conversion is done as ((num & 0xff) >> 8) | (num << 8), gcc 4.8.3 generates a single rol instruction. And if 32 bit conversion is written as ((num & 0xff000000) >> 24) | ((num & 0x00ff0000) >> 8) | ((num & 0x0000ff00) << 8) | (num << 24), same compiler generates a single bswap instruction.Lockyer
I dont know how efficient this is but Ive swapped the byte order with bitfields like this: 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;} where this is a bitfield with 8 fields 1 bit each. But I am not sure if thats as fast as the other suggestions. For ints use the union { int i; byte_t[sizeof(int)]; } to reverse byte by byte in the integer.Overweening
I think the expression must be: (num >> 8) | (num << 8) to reverse the byte order and NOT: ((num & 0xff) >> 8) | (num << 8), The wrong example gets zero in the low-byte.Cognation
@IlianZapryanov Maybe +1 for clarity but using bitfields in C like that is probably the least efficient way to do it.Rayfordrayle
S
126

By including:

#include <byteswap.h>

you can get an optimized version of machine-dependent byte-swapping functions. Then, you can easily use the following functions:

__bswap_32 (uint32_t input)

or

__bswap_16 (uint16_t input)
Smalls answered 5/8, 2011 at 18:52 Comment(10)
Thanks for your answer, but I can not use any library functionJournalese
Should read #include <byteswap.h>, see comment in the .h file itself. This post contains helpful information so I up-voted despite the author ignoring the OP requirement to not use a lib function.Banksia
In fact, the __bswap_32/__bswap_16 functions are in fact macros and not library functions, another reason to up-vote.Banksia
Where can I find this header? The only I got was LGPL, and doesn't look one of standard header. It means it may not available on LGPL-incompatible system and I can't copy it into the system.Luttrell
My understanding is that this header is not guaranteed to exist for all operating systems on all architectures. I have yet to find a portable way to deal with endian issues.Neuralgia
While I prefer the accepted answer, no constraint on portability was requested, so this answer is legit in my books.Peggy
doesn't exist on windows - at least not when cross compiling from linux with mingw 32 or 64 bitApocope
Doesn't exist on mac too.Confederacy
#include <byteswap.h> is not part of the C standard library.Melodie
This solution is non-portable and also ignores the request that the solution is not a library function.Filiate
B
77
#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);
}

Update : Added 64bit byte swapping

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);
}
Bedaub answered 14/4, 2010 at 12:4 Comment(5)
For the int32_t and int64_t variants, what is the reasoning behind the masking of ... & 0xFFFF and ... & 0xFFFFFFFFULL? Is there something going on with sign-extension here I'm not seeing? Also, why is swap_int64 returning uint64_t? Shouldn't that be int64_t?Colligan
The swap_int64 returning a uint64 is indeed an error. The masking with signed int values is indeed to remove the sign. Shifting right injects the sign bit on the left. We could avoid this by simply calling the unsigned int swapping operation.Bedaub
Thanks. You might want to change the type of the return value for swap_int64 in your answer. +1 for the helpful answer, BTW!Colligan
Is the bitwise and value endian dependent?Tartaric
The LL are unnecessary in (u)swap_uint64() much like an L is not needed in (u)swap_uint32(). The U is not needed in uswap_uint64() much like the U is not needed in uswap_uint32()Melodie
T
20

Here's a fairly generic version; I haven't compiled it, so there are probably typos, but you should get the 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: This is not optimised for speed or space. It is intended to be clear (easy to debug) and portable.

Update 2018-04-04 Added the assert() to trap the invalid case of n == 0, as spotted by commenter @chux.

Tannenbaum answered 2/2, 2010 at 8:2 Comment(10)
you can use xorSwap for better performance. Prefer this generic version above all the size specific ones...Emulous
I tested it, it turns out this is faster than xorSwap... on x86. #3128595Emulous
@nus -- One of the advantages of very simple code is that the compiler optimiser can sometimes make it very fast.Tannenbaum
@MichaelJ OTOH, the 32 bit version above in chmike's answer gets compiled to a single bswap instruction by a decent X86 compiler with optimisation enabled. This version with a parameter for the size couldn't do that.Sudden
@Sudden - As I said, I made no effort tp optimise my code. When user nus found that the code ran very fast (in one case) I just mentioned the general idea that simple code can often be highly optimised by a compiler. My code works for a wide variety of cases and it is pretty easy to understand and thus easy to debug. That met my objectives.Tannenbaum
I'd expect SwapBytes() to nicely handle the corner case of SwapBytes(pv, 0). With this code, SwapBytes(pv, 0) leads to UB.Melodie
@chux -- That is not exaactly an edge case as an out of range, but I agree it is a problem. Perhaps the root of the problem is that I made an assumption (n must be >0) without documenting or testing that assumption. I shall shortly amend the code. It is a great reminder of the value of code review. Even very simple code can contain subtle problems.Tannenbaum
Perhaps for(lo=0, hi=n; lo<hi; ) { char tmp=a[--hi]; a[hi] = a[lo]; a[lo++] = tmp; } to handle the zero case/Melodie
declaration of char temp inside the for loop, not the efficient way.Component
@ Ayush joshi - I was not trying to be efficient, but simple and easy to understand. Notwithstanding that, I would expect an optimising compiler to handle that without any loss of efficiency. Only on an embedded system or similarly constrained environment would I worry about that.Tannenbaum
G
11

Edit: These are library functions. Following them is the manual way to do it.

I am absolutely stunned by the number of people unaware of __byteswap_ushort, __byteswap_ulong, and __byteswap_uint64. Sure they are Visual C++ specific, but they compile down to some delicious code on x86/IA-64 architectures. :)

Here's an explicit usage of the bswap instruction, pulled from this page. Note that the intrinsic form above will always be faster than this, I only added it to give an answer without a library routine.

uint32 cq_ntohl(uint32 a) {
    __asm{
        mov eax, a;
        bswap eax; 
    }
}
Ginsburg answered 2/2, 2010 at 6:26 Comment(12)
For a C question, you're suggesting something that's specific to Visual C++?Organization
@Alok: Visual C++ is a product by Microsoft. It works just fine for compiling C code. :)Ginsburg
Why does it stun you that many people aren't aware of Microsoft-specific implementations of byteswapping?Bandolier
@dreamlax: Because I've worked in commercial environments that use Visual Studio as the official IDE for the better part of a decade at quite a few companies (contract work).Ginsburg
Cool, that's good info for anyone developing a closed source product which doesn't need to be portable or standards compliant.Phenanthrene
@280Z28: OK, but I think that the question is more general than your answer, and I hope you agree. Not that the question is very good to begin with :-)Organization
@280Z28: I guess non-portable methods of byteswapping has been a very common task at the places you have worked.Bandolier
the question says no library functionsFallacy
@John Knoeller: which is why I added the manual implementation. :) It says no library functions but doesn't rule out inline assembly.Ginsburg
@280z28: can't beat bswap. If you are going to go MS specific, might as well go whole hog uint32 __fastcall(uint32 a) { __asm { mov eax, edx; bswap eax; } }Fallacy
@John Knoeller: the intrinsics annihilate the performance of that call, which is why I didn't bother to do anything past what's up there.Ginsburg
@Alok, OP did not mention the compiler|OS. A person is allowed to give answers according to his experience with a particular set of tools.Ducal
P
11

If you need macros (e.g. embedded system):

#define SWAP_UINT16(x) (((x) >> 8) | ((x) << 8))
#define SWAP_UINT32(x) (((x) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | ((x) << 24))
Philbrook answered 23/5, 2013 at 7:47 Comment(3)
These macros are fine, but ((x) >> 24) will fail when a signed integer is between 0x80000000 and 0xffffffff. It's a good idea to use bitwise AND here. Note: ((x) << 24) is perfectly safe. (x) >> 8) will also fail if high 16 bits are nonzero (or a signed 16-bit value is provided).Prosaism
@PacMan-- These macros are intended to be used to swap unsigned integers only. That's why there is the UINT in their name.Philbrook
Yes, true, sorry for the noise. Wouldn't it be best to embed a typecast ?Prosaism
B
5

As a joke:


#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;
}
Bandolier answered 2/2, 2010 at 6:47 Comment(4)
HAHAHAHAHA. Hahaha. Ha. Ha? (What joke?)Varistor
did you pull this from some Windows source repository? :)Governorship
Nodejs uses this technique! github.com/nodejs/node/blob/…Telegony
Curious to use int i, size_t sizeofInt and not the same type for both.Melodie
C
5

here's a way using the SSSE3 instruction pshufb using its Intel intrinsic, assuming you have a multiple of 4 ints:

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;
}
Creuse answered 7/7, 2013 at 5:44 Comment(0)
N
3

Will this work / be faster?

 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];
Nightdress answered 4/8, 2010 at 21:30 Comment(2)
I think you mean char, not byte.Bandolier
Using this strategy, the solution with most votes compared to yours is equivalent and the most efficient and portable. However the solution I propose (second most votes) needs less operations and should be more efficient.Bedaub
P
3

This code snippet can convert 32bit little Endian number to Big Endian number.

#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);    
}
Pukka answered 2/7, 2013 at 11:5 Comment(2)
Thanks @YuHao I am new here, don't know how to format the Text.Pukka
Using ((i>>24)&0xff) | ((i>>8)&0xff00) | ((i&0xff00)<<8) | (i<<24); might be faster on some platforms (eg. recycling the AND mask constants). Most compilers would do this, though, but some simple compilers are not able to optimize it for you.Prosaism
O
1

EDIT: This function only swaps the endianness of aligned 16 bit words. A function often necessary for UTF-16/UCS-2 encodings. EDIT END.

If you want to change the endianess of a memory block you can use my blazingly fast approach. Your memory array should have a size that is a multiple of 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);
}

This kind of function is useful for changing the endianess of Unicode UCS-2/UTF-16 files.

Olly answered 2/2, 2010 at 9:43 Comment(5)
CHAR_BIT #define is missing to make code complete.Agni
Ok, I added the missing includes.Fraxinella
here is a link to a swap in C++ , I dont know if its as fast as the suggestions but it wokrs: github.com/heatblazer/helpers/blob/master/utils.hOverweening
CHAR_BIT instead of 8 is curious as 0xFF00FF00FF00FF00ULL is dependent on CHAR_BIT == 8. Note that LL not needed in the constant.Melodie
You're right chux. Only wrote with CHAR_BIT to augment the exposure of that macro. As for the LL, it's more an annotation than anything else. It's also a habit I catched from a long time ago with buggy compilers (pre standard) which would not do the right thing.Fraxinella
J
1

Here's a function I have been using - tested and works on any basic data type:

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

}
Juxon answered 31/8, 2012 at 15:19 Comment(1)
Code relies on a very reasonable assumption: source is aligned as needed - yet if that assumption does not hold, the code is UB.Melodie
T
1

Stumbled upon a Reverse an N-bit quantity in parallel in 5 * lg(N) operations solution.

unsigned int v; // 32-bit word to reverse bit order

// swap odd and even bits
v = ((v >> 1) & 0x55555555) | ((v & 0x55555555) << 1);
// swap consecutive pairs
v = ((v >> 2) & 0x33333333) | ((v & 0x33333333) << 2);
// swap nibbles ... 
v = ((v >> 4) & 0x0F0F0F0F) | ((v & 0x0F0F0F0F) << 4);
// swap bytes
v = ((v >> 8) & 0x00FF00FF) | ((v & 0x00FF00FF) << 8);
// swap 2-byte long pairs
v = ( v >> 16             ) | ( v               << 16);

In this a variation with removing dependencies on constants is provided which is also O(log(n)).

#define CHAR_BIT (8) 
uint64_t reverse_bits(uint64_t v) {
    uint64_t mask = ~0;
    uint64_t s = sizeof(uint64_t) * CHAR_BIT;
    while((s >>= 1) > 0) {
        mask ^= (mask << s);
        v = (((v >> s) & mask) | ((v << s) & ~mask));
    }
    return v;
}

If we closely look at the operations we see that first we swap the odd and even bits then we swap consecutive pairs then nibbles and then bytes and 2 bytes and so on, the code executes in reverse order. Now if we only restrict ourselves to swap until the bytes which is changing the endianness, a small change for while loop can result in quick implementation. (Change the (s >> 1) > 0 check condition). From this we can also reverse pairs, nibbles, bytes, 2 bytes and 4 bytes.

#define CHAR_BIT (8)

typedef enum {
    REVERSE_BITS    = 0,
    REVERSE_PAIRS   = 1,
    REVERSE_NIBBLES = 2,
    REVERSE_BYTES   = 4,
    REVERSE_WORDS   = 8,
    REVERSE_DWORDS  = 16,
} eReverseOrder;

uint64_t reverse_bit_order(uint64_t v, eReverseOrder r) {
    uint64_t mask = ~0;
    uint64_t s = sizeof(uint64_t) * CHAR_BIT;
    while((s >>= 1) > r) {
        mask ^= (mask << s);
        v = (((v >> s) & mask) | ((v << s) & ~mask));
    }
    return v;
}

uint64_t reverse_bits(uint64_t v) {
    return reverse_bit_order(v, REVERSE_BITS);
}

uint64_t reverse_pairs(uint64_t v) {
    return reverse_bit_order(v, REVERSE_PAIRS);
}

uint64_t reverse_nibbles(uint64_t v) {
    return reverse_bit_order(v, REVERSE_NIBBLES);
}

uint64_t reverse_bytes(uint64_t v) {
    return reverse_bit_order(v, REVERSE_BYTES);
}

uint64_t reverse_words(uint64_t v) {
    return reverse_bit_order(v, REVERSE_WORDS);
}

uint64_t reverse_dwords(uint64_t v) {
    return reverse_bit_order(v, REVERSE_DWORDS);
}
Tonsil answered 16/9, 2023 at 3:25 Comment(1)
This is a full bit-reverse, even within the bits of each byte. The question asked for just an endian swap, just reversing with 8-bit granularity. Also, don't define CHAR_BIT yourself!! #include <limits.h> to get the correct value for your compiler.Hippocampus
R
0

I am working on an STM32 project, and this code did NOT work for me for uint16_t data type:

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

But this is working:

#define REV32(n) ( ((n&0xff000000)>>24) | (((n&0x00ff0000)<<8)>>16) | (((n&0x0000ff00)>>8)<<16) | ((n&0x000000ff) << 24) )
#define REV16(n) ((((n) >> 8) & 0xff) | (((n) & 0xff) << 8))
Rubato answered 18/10, 2023 at 17:42 Comment(0)
F
-8

If the system is big endian:

For 16 bit values:

unsigned short big = value;
unsigned short little = ((big & 0xFF) << 8) | (big >> 8);

For 32 bit values:

unsigned int big = value;
unsigned int little = ((big & 0xFF) << 24)
                    | ((big & 0xFF00) << 8)
                    | ((big >> 8) & 0xFF00)
                    | (big >> 24);

This isn't the most efficient solution unless the compiler recognizes that this is byte level manipulation and generates byte swapping code. But it doesn't depend on any memory layout tricks and can be turned into a macro pretty easily.

Fallacy answered 2/2, 2010 at 6:5 Comment(1)
On x86 and x86_64 architectures the little endian scheme is the native one.Eran

© 2022 - 2024 — McMap. All rights reserved.