Luca
Luca

Reputation: 11006

WriteFile overlapped and fwrite equivalent

On Windows, the WriteFile() function has a parameter called lpOverlapped which lets you specify an offset at which to write to the file.

I was wondering, is there is an fwrite() cross-platform equivalent of that?

I see that if the file is opened with the rb+ flag, I might be able to use fseek() to write to a particular offset. My question is - will this approach be equivalent to the overlapped WriteFile(), and will it produce the same behaviour on all platforms?

Background

The reason I need this is because I am writing blocked compressed data streams to a file, and I want to be able to load a specific block from the file and be able to decompress it. So, basically if I keep track of where the block begins in a file, I can load the block and decompress it in a more efficient manner. I know that there are probably better ways to do this, but I need this solution for some backwards compatibility.

Upvotes: 1

Views: 1049

Answers (3)

ShadowRanger
ShadowRanger

Reputation: 155574

While pwrite is probably the best solution, there is an alternative that sticks with stdio functions. Unfortunately, to make it thread-safe, you're using non-standard "stdio" to take direct control of the FILE*'s internal lock, and the names aren't portable. Specifically, POSIX defines one set of "take/release file lock" names and Windows defines another set (_lock_file/_unlock_file).

That said, you could use these semi-portable constructs to use stdio functions to ensure no buffering conflicts (pwrite to fileno(some_FILE_star) could cause problems if the FILE* buffer overlaps the pwrite location, since pwrite won't fix up the buffer):

// Error checking omitted; you should actually check returns in real code
size_t pfwrite(const void *ptr, size_t size, size_t n,
               size_t offset, FILE *stream) {
    // Take FILE*'s lock and hold it for entire transaction
    flockfile(stream);  // _lock_file on Windows

    // Record position
    long origpos = ftell(stream);

    // Seek to desired offset and write
    fseek(stream, offset, SEEK_SET); // Possibly offset * size, not just offset?
    size_t written = fwrite(ptr, size, n, stream);

    // Seek back to original position
    fseek(stream, origpos, SEEK_SET);

    // Release FILE*'s lock now that transaction complete
    funlockfile(stream); // _unlock_file on Windows
    return written;
}

Upvotes: 2

Travis Gockel
Travis Gockel

Reputation: 27673

Assuming you are okay with using POSIX functions and not just things from the C or C++ standard libraries, the solution is pwrite (aka: positioned write).

ssize_t rc = pwrite(file_handle, data_ptr, data_size, destination_offset);

Upvotes: 4

Mike Layton
Mike Layton

Reputation: 101

I think you are confusing "overlapped" and "overwrite"/"offset." I didn't study up on the specifics of why Microsoft explicitly says overlapped writes include a parameter for offset (I think it makes sense as I describe below). In general, when Microsoft talks about "overlapped" IO, they are talking about how to synchronize events like starting to write the file, receiving notification that the write completed, and starting another write to the file which might or might not overlap with a previous write. In this last case, by overlap I mean what you would think that overlap means, ie overlaps within the contents of the file. Whereas Microsoft means that writing the file overlaps in time with your thread running, or not. Note that this gets very complicated if more than one thread can write the same file.

If possible, and surely if you want portable code, you want to avoid all this nonsense and just do the simplest write possible in each context, which means avoid Microsoft optimizations like "overlapped IO" unless you really need performance. (And if you need absolutely optimal performance, you might want to cache the file yourself and manage the overlaps, then write it once from start to finish.)

Upvotes: 2

Related Questions