What this function supposedly does is write a 32bit unsigned integer to a file stream in little-endian.
What it really does is, instead of doing one small simple task and getting it right, it tries to do two things together and gets both wrong.
Before I go into why this is wrong, I'll quickly answer the question itself:
- Why
UINT32
? Don't know. It's non standard and better be replaced with uint32_t
.
- Why
0xff
? Used as a mask to keep the lower 8 bits in a larger variable (32 bits)
- What are 4 right shifts doing here? The shifts are used to bring every byte in the original UINT to the rightmost bits in the correct "little-endian" order.
Now, with that out of the way...
Why this is wrong?
A char
is guaranteed to be at least 8 bits wide, but it could be larger. This means that fputc
will probably write 8 bits per call, but it might write more. What this means is that on a certain architecture where a char is (for example) 16 bits, the function will write 64 bits per call and it won't be little-endian at all. Additionally, the function is very careful checking for errors but doesn't expose the relevant failure information to the caller (how many bytes were written?).
What's a better design?
First, as always, do one small thing and do it right:
uint32_t toLittleEndian(uint32_t v)
{
uint32_t result = v >> 24;
result |= v << 24;
result |= (v >> 8) & 0xf0;
result |= (v << 8) & 0xf00;
return result;
}
Note: this implementation is deliberately verbose. There's an optimisation opportunity using bit rotation operations but C has no native support for that and requires assembly code
This function only converts a big-endian 32 bit value to its equivalent little-endian representation (actually it alternates between the two and could be named switchEndian
).
Next, we need to write the value to a file:
int write(uint32_t v, FILE *f)
{
return fwrite(&v, sizeof(v), 1, f);
}
This will write exactly 32 bits wide integer, and return the number of bytes actually written. Additionally, it's reusable.
Next, we could create a convenience composite function if we want:
int writeLittleEndian(uint32_t v, FILE *f)
{
return write(toLittleEndian(v), f);
}