Wine and endianness

David Laight david at l8s.co.uk
Mon Aug 1 02:09:47 CDT 2011


On Mon, Aug 01, 2011 at 08:31:50AM +0200, GOUJON Alexandre wrote:
> 
> Sorry to disturb your conversation but the subject is worth discussing.
> 
> I'm currently trying to add UDF support on wine based on Steven Wallace 
> work.
> Quoting the specification : "On the media the UDF structures are stored 
> little endian" as windows API.
> But wine is not limited on x86 so what's the rule ?
> Currently, my code is
> 
>     int i = 1;
>     char *p = (char *)&i;
>     BOOL isBigEndian = p[0]==1;
> 
>     /* Tag location (Uint32) at offset 12, little-endian */
>     *offs  = (bloc[20+0] & 0xFF) << ( isBigEndian ?  0 : 24);
>     *offs += (bloc[20+1] & 0xFF) << ( isBigEndian ?  8 : 16);
>     *offs += (bloc[20+2] & 0xFF) << ( isBigEndian ? 16 :  8);
>     *offs += (bloc[20+3] & 0xFF) << ( isBigEndian ? 24 :  0);
> 
> Is it correct ?

Depends what the data you are playing with is....
But it looks a bit dubious to me.

> Any thoughts ?

1) You want endianness to be a compile time constant, not run time.
2) If that code is trying to read a 32bit LE value from a disk sector
   it is wrong.
3) If you care how fast the code runs, don't repeatedly write through
   a pointer.

If you have 'unsigned char bloc[]' and want to read a 32 bit LE value
you can do:
    value = bloc[20 + 0];
    value |= bloc[20 + 1] << 8;
    value |= bloc[20 + 2] << 16;
    value |= bloc[20 + 3] << 24;
    *offs = value;
And that is correct on all architectures.

To write a LE value use:
    buf[0] = value;
    buf[1] = value >>= 8;
    buf[2] = value >>= 8;
    buf[3] = value >>= 8;

(For BE reverse the indexes)

If the item is known to be correctly aligned (or the architecture supports
mis-aligned reads) then you can just do (eg):
    value = *(uint32_t *)(bloc + 20);
    value = le32toh(value);
but you'll have to chase around the OS headers to find how to spell le32toh().
If you define a C struct that matches the data area (packed if it might
have misaligned data), the compiler will generate the shifts and masks to
read a native word - so you only need the le32toh() macro.
(le32toh() is from NetBSD, can't remember what Linux calls it!)

It is also worth noting that some architectures (eg ppc) have instructions
for reading and writing memory with byteswap, but no instruction for
swapping a register. Newer gccs can be taught how to use these instructions
for specific source sequences.

	David

-- 
David Laight: david at l8s.co.uk



More information about the wine-devel mailing list