static NSData *PSPDFCalculateSHA256FromFileURL(NSURL *fileURL, CC_LONG dataLength, NSError **error) { | |
NSCParameterAssert(fileURL); | |
dispatch_queue_t shaQueue = dispatch_queue_create("com.pspdfkit.sha256-queue", DISPATCH_QUEUE_SERIAL); | |
__block dispatch_io_t readChannel; | |
void (^processIntError)(int intError) = ^(int intError) { | |
if (intError != 0) { | |
PSPDFLogWarning(@"Stream error: %d", intError); | |
if (error) *error = [NSError errorWithDomain:@"SHA256Error" code:100 userInfo:@{NSLocalizedDescriptionKey: @"failed to open file for calculating SHA256."}]; | |
} | |
dispatch_async(shaQueue, ^{ | |
dispatch_io_close(readChannel, DISPATCH_IO_STOP); | |
}); | |
}; | |
// Open the IO channel | |
readChannel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, fileURL.path.UTF8String, O_RDONLY, 0, shaQueue, processIntError); | |
if (!readChannel) { | |
if (error) *error = [NSError errorWithDomain:@"SHA256Error" code:100 userInfo:@{NSLocalizedDescriptionKey: @"failed to open file for calculating SHA256."}]; | |
return nil; | |
} | |
// Prepare SHA hash | |
__block CC_SHA256_CTX ctx; | |
CC_SHA256_Init(&ctx); | |
// Start read and block until we are done. | |
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); | |
__block int lastIntError = 0; | |
dispatch_io_set_high_water(readChannel, 512*1024); | |
dispatch_io_read(readChannel, 0, SIZE_MAX, shaQueue, ^(bool done, dispatch_data_t data, int intError) { | |
if (intError != 0) { | |
lastIntError = intError; | |
processIntError(intError); | |
dispatch_semaphore_signal(semaphore); | |
return; | |
} | |
dispatch_data_apply(data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) { | |
CC_SHA256_Update(&ctx, (const void *)buffer, (CC_LONG)size); | |
return true; | |
}); | |
if (done) { | |
dispatch_semaphore_signal(semaphore); | |
} | |
}); | |
// Wait for the signal, then wake up. | |
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); | |
dispatch_io_close(readChannel, DISPATCH_IO_STOP); | |
// Finalize SHA256 calculation unless there's an error. | |
if (lastIntError == 0) { | |
unsigned char sha256[CC_SHA256_DIGEST_LENGTH]; | |
CC_SHA256_Final(sha256, &ctx); | |
NSData *shaData = [NSData dataWithBytes:sha256 length:CC_SHA256_DIGEST_LENGTH]; | |
return shaData; | |
} | |
return nil; | |
} |
As mentioned on Twitter, looking at line 36
. Maybe you could remove the return and wrap the dispatch_data_apply
in an if condition checking if data is dispatch_data_empty
(from Apple doc:
"If an unrecoverable error occurs while performing the I/O
operation, the handler block will be submitted with the done flag set and the appriate POSIX error code
in the error parameter."
), so that when you get an error, you still enter the if (done)
check and signal the semaphore. But honestly I never did dispatch io before and probably I'm just missing something obvious :)
What for you pass dataLength
in this function. I can't find where it used.
@Vyazovoy It's the file size so you can hash over a part if you choose.
@steipete in line 36
you return, but in Apple docs I see: If an unrecoverable error occurs on the channel’s file descriptor, the done parameter is set to YES and an appropriate error value is reported in the handler’s error parameter
. So you return but semaphore doesn't signaled and thread not woken in line 50
@steipete , what for you use serial queue? From docs: Your block need not be reentrant. The system guarantees that only one instance of this block will be executed at any given time.
@Vyazovoy I fixed the error handling part (note: also I'm not actually shipping that code, ended up with a simpler/faster way)
Follow conversation back on some interesting points related to priority donation: https://twitter.com/catfish_man/status/621442751138172928