-
-
Save steipete/b22babbf3014e29c19f0 to your computer and use it in GitHub Desktop.
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; | |
} |
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)
As mentioned on Twitter, looking at line
36
. Maybe you could remove the return and wrap thedispatch_data_apply
in an if condition checking if data isdispatch_data_empty
(from Apple doc:), 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 :)