I'm having trouble understanding the offset variable provided to the data applier for a dispatch_io_read function call. I see that the documentation claims the offset is the logical offset from the base of the data object. Looking at the source code for the dispatch_data_apply function confirms that this variable always starts from 0 for the first apply for a data chunk, and then is simply the sum of the range lengths.
I guess I don't understand the purpose of this variable then. I had originally assumed this was the offset for the entire read, but it's not. It seems you have to keep track of the bytes read and offset by that amount to actually properly do a read in libdispatch.
// Outside the dispatch_io_read handler...
char * currBufferPosition = destinationBuffer;
// Inside the dispatch_io_read handler...
dispatch_io_read(channel, fileOffset, bytesRequested, queue, ^(bool done, dispatch_data_t data, int error) {
// Note: Real code would handle error variable.
dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void * buffer, size_t size) {
memcpy(currBufferPosition, buffer, size);
currBufferPosition += size;
return true;
});
});
My question is: Is this the right way of using the data returned by dispatch_data_apply? And if so, what is the purpose of the offset variable passed into the applier handler? The documentation does not seem clear about this to me.
A
dispatch_data_tis an sequence of bytes. The bytes can be stored in multiple non-contiguous byte arrays. For example, bytes 0-6 can be stored in an array, and then bytes 7-12 are stored in a separate array somewhere else in memory.For efficiency, the
dispatch_data_applyfunction lets you iterate over those arrays in-place (without copying out the data). On each call to your “applier”, you receive a pointer to one of the underlying storage arrays in thebufferargument. Thesizeargument tells you how many bytes are in this particular array, and theoffsetargument tells you how (logically) far the first byte of this particular array is from the first byte of the entiredispatch_data_t.Example:
Output:
Okay, so that's what the
offsetargument is for. Now how does this relate todispatch_io_read?Well,
dispatch_io_readdoesn't pass you the same bytes twice. Once it has passed you some bytes, it discards them. The next time it passes you bytes, they are fresh, newly-read bytes. If you want the old bytes, you have to keep them around yourself. If you want to know how many old bytes you were given before the current call to your callback, you have to keep that count yourself. That is not what theoffsetargument is for.It's possible that when
dispatch_io_readcalls you, it passes you adispatch_data_tthat has stored its bytes in multiple non-contiguous arrays, so when you calldispatch_data_applyon it, your applier gets called multiple times, with differentoffsets andbuffers andsizes. But those calls only get you access to the fresh new bytes for the current call to your callback, not to old bytes from any prior calls to your callback.