Is there any way to force fwrite() to write in big-endian format no matter what? I am trying to write a MIDI file and they are always big-endian. Converting every value I write to big endian beforehand would be extremely tedious. Ideas?

Dani AI

Generated

You cannot force fwrite() itself to change byte order — it writes whatever bytes you give it. A small, well-scoped serialization layer is the cleanest solution: provide write_be16, write_be32 and a write_varlen for MIDI delta-times, then call those from the places where you currently call fwrite/fputc. That follows 's wrapper idea and keeps the rest of the codebase endian-agnostic. Use fixed-width unsigned types (uint8_t, uint16_t, uint32_t) from <stdint.h> and avoid signed shifts.

Example implementations (simple, portable, and fwrite-friendly):

static void write_be16(FILE *f, uint16_t v) {
    uint8_t b[2];
    b[0] = (v >> 8) & 0xFF;
    b[1] = v & 0xFF;
    fwrite(b, 1, 2, f);
}

static void write_be32(FILE *f, uint32_t v) {
    uint8_t b[4];
    b[0] = (v >> 24) & 0xFF;
    b[1] = (v >> 16) & 0xFF;
    b[2] = (v >> 8) & 0xFF;
    b[3] = v & 0xFF;
    fwrite(b, 1, 4, f);
}

MIDI delta times use variable-length quantities (VLQ) with 7-bit groups and a continuation bit; they must be emitted most-significant-group first. A simple pattern is to collect 7-bit groups into a small temp buffer and write them in reverse order so the highest group goes out first.

Practical notes and tradeoffs: showed unions and tested bit shifts — unions work on many compilers but are less portable/clear than explicit shifts. was right that htonl/htons map to network (big-endian) order and can be convenient; on many platforms they are optimized no-ops on big-endian hosts. For maximum speed consider compiler intrinsics (__builtin_bswap32) or platform htobe16/htobe32 where available. Minimize syscall overhead by assembling bytes into a stack buffer and calling fwrite once per multi-byte field. Finally, validate output with a hex editor or MIDI parser to confirm chunk headers and lengths are correct.

Recommended Answers

All 8 Replies

write a wrapper function such as mywrite() and call it instead of fwrite().

How tedious it is depends on how many different types you need to reverse. If just ints:

typedef unsigned char uchar;

union IntBytes {
    int i;
    uchar b[sizeof(int)];
};

WriteInt( int n, FILE* fout ) {
    union IntBytes uBytes;
    int i;
    uBytes.i = n;
    for (i = sizeof(int) - 1; i >= 0; --i)
        fputc( uBytes.b[i], fout );
}

Would that solution be more or less efficient than calling a function like this on the data before writing:

void swapEndianL(unsigned int *x)
{
	*x = (*x>>24) |
	 ((*x << 8) & 0x00FF0000) |
	 ((*x >> 8) & 0x0000FF00) |
	 (*x << 24);
}

You should profile them. Post your results.

Well this isn't a terribly performance-intensive part of my program, and I have to submit the whole bloody thing by friday, so maybe i'll get to it another time, and post the results if anyone is still intersted.

Don't bother. Mine was "conceptually simple." Your bit-shifting solution is best. It's even a perfect candidate for coding in assembly if performance was an issue.

If you don't mind including <arpa/inet.h>, you can use the htonl() function:

int newval = htonl(origval);

The "h" on the front is for "host" and the "n" is for network, "l" on the end for "long" (there is also a version for 16-bit integers). Hence, "Host to network Long". Network format is big endian.

On platforms that are already big endian (like Solaris), it's defined as a NULL macro.

Is there any way to force fwrite() to write in big-endian format no matter what? I am trying to write a MIDI file and they are always big-endian. Converting every value I write to big endian beforehand would be extremely tedious. Ideas?

It would be a better idea to keep you code more portable to hanlde big endian/little endian environments. In either case, create a small library or API of functions that you can call before you execute IO. For example, you might have some custom functions that wrap the socket htons/l or ntohs/l functions and opearate on the stream or array or pointer/length you pass. Use that approach and YOU guarantee the format of the data you write to the disk, network, or other file system. You can also add encryption in this manner as well. Good luck

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.