Reputation: 11006
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
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
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
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