There is a code excerpt from official Quake 2 source code:
unsigned *buf;
dheader_t header;
...
header = *(dheader_t *)buf; // #1
for (i=0 ; i<sizeof(dheader_t)/4 ; i )
((int *)&header)[i] = LittleLong ( ((int *)&header)[i]); // #2
Can someone please explain me in the most possible details what do the line #1 and then #2 really do because I'm little or more confused...
P.S
Here is the rest of the definitions if it helps:
int LittleLong (int l) {return _LittleLong(l);}
...
typedef struct
{
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
P.S. 2
I've linked above the original full source file code if needed.
CodePudding user response:
This is some seriously brittle code and you shouldn't write code like this.
What it does is to go through the struct int
by int
, then does something with each such int
inside _LittleLong
. Very likely this function performs a 32 bit conversion from a big endian integer to a little endian one. Meaning that the source you are looking at is likely something related to reception of IP packages.
Checking at what the code does step by step:
for (i=0 ; i<sizeof(dheader_t)/4 ; i )
is a sloppier way of writingsizeof(dheader_t)/sizeof(int)
. That is: iterate through the structint
byint
, chunks of 32 bits.(int *)&header
converts from adheader_t*
to aint*
. This is actually well-defined by a special rule in C that allows us to convert from a pointer to a struct to a pointer to its first member or vice versa and the first member isint
.- However, doing so is only well-defined for the first member. Instead they take the converted
int*
and apply array dereferencing on it:((int *)&header)[i]
. This is undefined behavior in C, a so-called strict aliasing violation, and could also cause alignment problems in some situations. Bad. - The
int
read from the struct through this dereferencing is then passed along toLittleLong
which very likely does a big -> little endian conversion. ((int *)&header)[i] =
and here it is written back to where it was grabbed from.
Better, safer, well-defined and possibly faster code could look like:
void endianify (dheader_t* header)
{
_Static_assert(sizeof(dheader_t)%sizeof(uint32_t)==0,
"Broken struct: dheader_t");
unsigned char* start = (unsigned char*)header;
unsigned char* end = start sizeof(dheader_t);
for(unsigned char* i=start; i!=end; i =sizeof(uint32_t))
{
uint32_t tmp;
memcpy(&tmp,i,sizeof(uint32_t));
i[0]= (tmp >> 24) & 0xFF;
i[1]= (tmp >> 16) & 0xFF;
i[2]= (tmp >> 8) & 0xFF;
i[3]= (tmp >> 0) & 0xFF;
}
}
Disassembly:
endianify:
mov eax, DWORD PTR [rdi]
bswap eax
mov DWORD PTR [rdi], eax
mov eax, DWORD PTR [rdi 4]
bswap eax
mov DWORD PTR [rdi 4], eax
mov eax, DWORD PTR [rdi 8]
bswap eax
mov DWORD PTR [rdi 8], eax
mov eax, DWORD PTR [rdi 12]
bswap eax
mov DWORD PTR [rdi 12], eax
mov eax, DWORD PTR [rdi 16]
bswap eax
mov DWORD PTR [rdi 16], eax
ret