Skip to content

Instantly share code, notes, and snippets.

@steipete
Last active March 21, 2017 16:00
Show Gist options
  • Save steipete/b22babbf3014e29c19f0 to your computer and use it in GitHub Desktop.
Save steipete/b22babbf3014e29c19f0 to your computer and use it in GitHub Desktop.
Haven't done much with dispatch_io yet so I'd appreciate a few more eyeballs on this. Am I closing things correctly in all error conditions? Are there other knobs I could change to make things even faster? (I know I've been lazy on the NSError's)
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;
}
@Vyazovoy
Copy link

@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

@Vyazovoy
Copy link

@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.

@steipete
Copy link
Author

@Vyazovoy I fixed the error handling part (note: also I'm not actually shipping that code, ended up with a simpler/faster way)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment