Home page > Debian > Byte swap and amd64..

Byte swap and amd64..

Friday 15 August 2008, by Toots

Some really weird issue just poped to my eyes today, while coding some big->little endian code for liquidsoap.

We have a 2 bytes swaping code that does the following:

((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8)

However, this is not working at all on my amd64/x86_64 box (core2duo CPU). I get a horrible saturated sound..

So, I copy-pasted this from oss4:

static inline short
bswap_16 (short x)
{
 short y = 0;
 unsigned char *a = ((unsigned char *) &x) + 1;
 unsigned char *b = (unsigned char *) &y;

 *b++ = *a--;
 *b++ = *a--;

 return y;
}

.. which is working.. !

So, I’m stuck with one function that I understand, should be working, but isn’t, and one that I don’t understand, but is working..

May a C guru give me some pointers about this issue ? I believe it’s connected to some sort of architecture specific issue... Thanks in advance !!

PS: the bswap_16 function included in byteswap.h from libc6-dev doesn’t work either, and yields the same saturated sound..

7 Forum messages

  • Byte swap and amd64.. Le 17 August 2008 à 17:50 , by MadCoder

    Despite ((x) & 0x00ff) << 8) | ((x) & 0xff00) >> 8) being inefficient with respect to what asm can do here (look at /usr/include/byteswap.h on a glibc), it’s also not working if ’x’ is of the wrong type.

    Defined as a macro it won’t work for some cases, but this will always work:

    static inline uint16_t bswap32(uint16_t x)

    /* Note that the masks are useless once `x’ is properly typed */

    return (x << 8) | (x >> 8);

    The reason is that if ’x’ is a signed int16_t, the right shift will propagate the caret on most architectures (right shift of signed types is undefined in plain C though).

    PS: your blog suck big time, as it mangles what is posted horribly, I’ve been unable to guess how to make the brackets from the above function be rendered.

    Reply to this message

  • Byte swap and amd64.. Le 18 August 2008 à 19:09

    The thing is that this specific code from OSS4 is not compatible with C99 anymore, because it breaks the strict aliasing rules: pointers of different types may not point to the same memory location, or otherwise the compiler might end up doing optimizations that are actually not allowed.

    The fix is to use unions instead. But in this case event that would be crazy. Use bswap_16 and friends. Everything else is slower and unnecessarily complicated.

    Reply to this message

    • Byte swap and amd64.. Le 18 August 2008 à 20:25 , by bd_

      unsigned char is exempt from strict aliasing - you may use an unsigned char * to inspect and/or modify any other type’s memory. Of course, the C standard doesn’t define the representation of any types, but it does add this requirement so that memcpy can be implemented in pure C, rather than needing evil compiler magic.

      Reply to this message

  • Byte swap and amd64.. Le 18 August 2008 à 20:22 , by bd_

    Rather than bswap_16, I’d use ntohs() - this is much more portable, both to other OSes, and to other CPU types (it automatically becomes a no-op on big-endian CPUs). It also uses an efficient inline-assembler implementation on many cpus (as does bswap_16)

    Reply to this message

    • Byte swap and amd64.. Le 19 August 2008 à 07:41 , by Jan Hudec

      ntohs converts from big endian to host endian — which means does nothing if host endian happens to be big. It could even be more appropriate (I don’t know this particular use-case), but definitely is not equivalent.

      Reply to this message

  • Byte swap and amd64.. Le 19 August 2008 à 05:53 , by zben

    The shift in ((x) & 0xff00) >> 8 could be either logical or arithmetic, with an arithmetic shift the sign bit extends into the shifted bits. This would be MUCH BETTER coded as

    ((x)>>8) & 0x00ff

    as this GUARANTEES the top bits will be zeroed. Of course, the one first given only screws up on architectures where >> is an arithmetic shift (which C allows, go read the spec!) and when x happens to be "negative" (when the high bit is set...)

    - Someone who has an earned doctorate in computer science.

    Reply to this message

    • Byte swap and amd64.. Le 19 August 2008 à 07:46 , by Jan Hudec

      ((x) >> 8) & 0x00ff is no better than ((x) & 0xff00) >> 8) — the low byte will still be incorrect if x is of a signed type. Inline function or explicit cast to uint_16t (which usually happens to be unsigned short) is needed.

      Reply to this message

Reply to this article