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 structintbyint, chunks of 32 bits.(int *)&headerconverts 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
intread from the struct through this dereferencing is then passed along toLittleLongwhich 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
