Skip to content

Commit 69ddb09

Browse files
authored
Merge pull request #371 from iOverlander/master
Fix out of memory issues hashing large files on iOS
2 parents aa61c9d + 4bb9ea7 commit 69ddb09

File tree

1 file changed

+94
-7
lines changed

1 file changed

+94
-7
lines changed

ios/ReactNativeBlobUtilFS.mm

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,15 @@ + (void) readFile:(NSString *)path
581581

582582
# pragma mark - hash
583583

584+
typedef enum {
585+
HashAlgorithmMD5,
586+
HashAlgorithmSHA1,
587+
HashAlgorithmSHA224,
588+
HashAlgorithmSHA256,
589+
HashAlgorithmSHA384,
590+
HashAlgorithmSHA512,
591+
} HashAlgorithm;
592+
584593
+ (void) hash:(NSString *)path
585594
algorithm:(NSString *)algorithm
586595
resolver:(RCTPromiseResolveBlock)resolve
@@ -611,7 +620,11 @@ + (void) hash:(NSString *)path
611620
return;
612621
}
613622

614-
NSData *content = [[NSFileManager defaultManager] contentsAtPath:path];
623+
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:path];
624+
if (fileHandle == nil) {
625+
reject(@"EUNKNOWN", [NSString stringWithFormat:@"Error opening '%@' for reading", path], error);
626+
return;
627+
}
615628

616629
NSArray *keys = [NSArray arrayWithObjects:@"md5", @"sha1", @"sha224", @"sha256", @"sha384", @"sha512", nil];
617630

@@ -634,23 +647,97 @@ + (void) hash:(NSString *)path
634647

635648
unsigned char buffer[digestLength];
636649

650+
const NSUInteger chunkSize = 1024 * 1024; // 1 Megabyte
651+
NSData *dataChunk;
652+
653+
CC_MD5_CTX md5Context;
654+
CC_SHA1_CTX sha1Context;
655+
CC_SHA256_CTX sha256Context;
656+
CC_SHA512_CTX sha512Context;
657+
HashAlgorithm hashAlgorithm;
658+
637659
if ([algorithm isEqualToString:@"md5"]) {
638-
CC_MD5(content.bytes, (CC_LONG)content.length, buffer);
660+
CC_MD5_Init(&md5Context);
661+
hashAlgorithm = HashAlgorithmMD5;
639662
} else if ([algorithm isEqualToString:@"sha1"]) {
640-
CC_SHA1(content.bytes, (CC_LONG)content.length, buffer);
663+
CC_SHA1_Init(&sha1Context);
664+
hashAlgorithm = HashAlgorithmSHA1;
641665
} else if ([algorithm isEqualToString:@"sha224"]) {
642-
CC_SHA224(content.bytes, (CC_LONG)content.length, buffer);
666+
CC_SHA224_Init(&sha256Context);
667+
hashAlgorithm = HashAlgorithmSHA224;
643668
} else if ([algorithm isEqualToString:@"sha256"]) {
644-
CC_SHA256(content.bytes, (CC_LONG)content.length, buffer);
669+
CC_SHA256_Init(&sha256Context);
670+
hashAlgorithm = HashAlgorithmSHA256;
645671
} else if ([algorithm isEqualToString:@"sha384"]) {
646-
CC_SHA384(content.bytes, (CC_LONG)content.length, buffer);
672+
CC_SHA384_Init(&sha512Context);
673+
hashAlgorithm = HashAlgorithmSHA384;
647674
} else if ([algorithm isEqualToString:@"sha512"]) {
648-
CC_SHA512(content.bytes, (CC_LONG)content.length, buffer);
675+
CC_SHA512_Init(&sha512Context);
676+
hashAlgorithm = HashAlgorithmSHA512;
649677
} else {
650678
reject(@"EINVAL", [NSString stringWithFormat:@"Invalid algorithm '%@', must be one of md5, sha1, sha224, sha256, sha384, sha512", algorithm], nil);
651679
return;
652680
}
653681

682+
while (true) {
683+
@autoreleasepool {
684+
dataChunk = [fileHandle readDataUpToLength:chunkSize error:&error];
685+
686+
if (error) {
687+
return reject(@"EREAD", [NSString stringWithFormat:@"Error reading file '%@'", path], error);
688+
break;
689+
}
690+
691+
if (dataChunk == nil || dataChunk.length == 0) {
692+
break;
693+
}
694+
695+
switch(hashAlgorithm) {
696+
case HashAlgorithmMD5:
697+
CC_MD5_Update(&md5Context, [dataChunk bytes], CC_LONG([dataChunk length]));
698+
break;
699+
case HashAlgorithmSHA1:
700+
CC_SHA1_Update(&sha1Context, [dataChunk bytes], CC_LONG([dataChunk length]));
701+
break;
702+
case HashAlgorithmSHA224:
703+
CC_SHA224_Update(&sha256Context, [dataChunk bytes], CC_LONG([dataChunk length]));
704+
break;
705+
case HashAlgorithmSHA256:
706+
CC_SHA256_Update(&sha256Context, [dataChunk bytes], CC_LONG([dataChunk length]));
707+
break;
708+
case HashAlgorithmSHA384:
709+
CC_SHA384_Update(&sha512Context, [dataChunk bytes], CC_LONG([dataChunk length]));
710+
break;
711+
case HashAlgorithmSHA512:
712+
CC_SHA512_Update(&sha512Context, [dataChunk bytes], CC_LONG([dataChunk length]));
713+
break;
714+
}
715+
716+
dataChunk = nil;
717+
}
718+
}
719+
720+
switch(hashAlgorithm) {
721+
case HashAlgorithmMD5:
722+
CC_MD5_Final(buffer, &md5Context);
723+
break;
724+
case HashAlgorithmSHA1:
725+
CC_SHA1_Final(buffer, &sha1Context);
726+
break;
727+
case HashAlgorithmSHA224:
728+
CC_SHA224_Final(buffer, &sha256Context);
729+
break;
730+
case HashAlgorithmSHA256:
731+
CC_SHA256_Final(buffer, &sha256Context);
732+
break;
733+
case HashAlgorithmSHA384:
734+
CC_SHA384_Final(buffer, &sha512Context);
735+
break;
736+
case HashAlgorithmSHA512:
737+
CC_SHA512_Final(buffer, &sha512Context);
738+
break;
739+
}
740+
654741
NSMutableString *output = [NSMutableString stringWithCapacity:digestLength * 2];
655742
for(int i = 0; i < digestLength; i++)
656743
[output appendFormat:@"%02x",buffer[i]];

0 commit comments

Comments
 (0)