YYFileHash.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. //
  2. // YYFileHash.m
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 14/11/2.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYFileHash.h"
  12. #include <CommonCrypto/CommonCrypto.h>
  13. #include <zlib.h>
  14. #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
  15. #define BUF_SIZE (1024 * 512) //512 KB per read
  16. #define BLOCK_LOOP_FACTOR 16 // 8MB (0.5MB*16) per block callback
  17. #define BUF_SIZE_NO_PROGRESS (1024 * 1024) // 1MB
  18. #else
  19. #define BUF_SIZE (1024 * 1024 * 16) //16MB per read
  20. #define BLOCK_LOOP_FACTOR 16 // 64MB (16MB*16) per block callback
  21. #define BUF_SIZE_NO_PROGRESS (1024 * 1024 * 16) // 16MB
  22. #endif
  23. @implementation YYFileHash
  24. + (YYFileHash *)hashForFile:(NSString *)filePath types:(YYFileHashType)types {
  25. return [self hashForFile:filePath types:types usingBlock:nil];
  26. }
  27. + (YYFileHash *)hashForFile:(NSString *)filePath types:(YYFileHashType)types usingBlock:(void(^)(UInt64 totalSize, UInt64 processedSize, BOOL *stop))block {
  28. YYFileHash *hash = nil;
  29. BOOL stop = NO, done = NO;
  30. int64_t file_size = 0, readed = 0, loop = 0;
  31. const char *path = 0;
  32. FILE *fd = 0;
  33. char *buf = NULL;
  34. int hash_type_total = 10;
  35. void *ctx[hash_type_total];
  36. int(*ctx_init[hash_type_total])(void *);
  37. int(*ctx_update[hash_type_total])(void *, const void *, CC_LONG);
  38. int(*ctx_final[hash_type_total])(unsigned char *, void *);
  39. long digist_length[hash_type_total];
  40. unsigned char *digest[hash_type_total];
  41. for (int i = 0; i < hash_type_total; i++) {
  42. ctx[i] = NULL;
  43. ctx_init[i] = NULL;
  44. ctx_update[i] = NULL;
  45. ctx_final[i] = NULL;
  46. digist_length[i] = 0;
  47. digest[i] = 0;
  48. }
  49. #define init_hash(Type,Init,Update,Final,Length) \
  50. ctx[ctx_index] = malloc(sizeof(Type)); \
  51. ctx_init[ctx_index] = (int (*)(void *))Init; \
  52. ctx_update[ctx_index] = (int (*)(void *, const void *, CC_LONG))Update; \
  53. ctx_final[ctx_index] = (int (*)(unsigned char *, void *))Final; \
  54. digist_length[ctx_index] = Length;
  55. int ctx_index = 0;
  56. if (types & YYFileHashTypeMD2) {
  57. init_hash(CC_MD2_CTX, CC_MD2_Init, CC_MD2_Update, CC_MD2_Final, CC_MD2_DIGEST_LENGTH);
  58. }
  59. ctx_index++;
  60. if (types & YYFileHashTypeMD4) {
  61. init_hash(CC_MD4_CTX, CC_MD4_Init, CC_MD4_Update, CC_MD4_Final, CC_MD4_DIGEST_LENGTH);
  62. }
  63. ctx_index++;
  64. if (types & YYFileHashTypeMD5) {
  65. init_hash(CC_MD5_CTX, CC_MD5_Init, CC_MD5_Update, CC_MD5_Final, CC_MD5_DIGEST_LENGTH);
  66. }
  67. ctx_index++;
  68. if (types & YYFileHashTypeSHA1) {
  69. init_hash(CC_SHA1_CTX, CC_SHA1_Init, CC_SHA1_Update, CC_SHA1_Final, CC_SHA1_DIGEST_LENGTH);
  70. }
  71. ctx_index++;
  72. if (types & YYFileHashTypeSHA224) {
  73. init_hash(CC_SHA256_CTX, CC_SHA224_Init, CC_SHA224_Update, CC_SHA224_Final, CC_SHA224_DIGEST_LENGTH);
  74. }
  75. ctx_index++;
  76. if (types & YYFileHashTypeSHA256) {
  77. init_hash(CC_SHA256_CTX, CC_SHA256_Init, CC_SHA256_Update, CC_SHA256_Final, CC_SHA256_DIGEST_LENGTH);
  78. }
  79. ctx_index++;
  80. if (types & YYFileHashTypeSHA384) {
  81. init_hash(CC_SHA512_CTX, CC_SHA384_Init, CC_SHA384_Update, CC_SHA384_Final, CC_SHA384_DIGEST_LENGTH);
  82. }
  83. ctx_index++;
  84. if (types & YYFileHashTypeSHA512) {
  85. init_hash(CC_SHA512_CTX, CC_SHA512_Init, CC_SHA512_Update, CC_SHA512_Final, CC_SHA512_DIGEST_LENGTH);
  86. }
  87. ctx_index++;
  88. if (types & YYFileHashTypeCRC32) {
  89. init_hash(uLong, crc32_init, crc32_update, crc32_final, sizeof(uint32_t));
  90. }
  91. ctx_index++;
  92. if (types & YYFileHashTypeAdler32) {
  93. init_hash(uLong, adler32_init, adler32_update, adler32_final, sizeof(uint32_t));
  94. }
  95. #undef init_hash
  96. int hash_type_this = 0;
  97. for (int i = 0; i < hash_type_total; i++) {
  98. if (digist_length[i]) {
  99. hash_type_this++;
  100. digest[i] = malloc(digist_length[i]);
  101. if (digest[i] == NULL || ctx[i] == NULL) goto cleanup;
  102. }
  103. }
  104. if (hash_type_this == 0) goto cleanup;
  105. buf = malloc(block ? BUF_SIZE : BUF_SIZE_NO_PROGRESS);
  106. if (!buf) goto cleanup;
  107. if (filePath.length == 0) goto cleanup;
  108. path = [filePath cStringUsingEncoding:NSUTF8StringEncoding];
  109. fd = fopen(path, "rb");
  110. if (!fd) goto cleanup;
  111. if (fseeko(fd, 0, SEEK_END) != 0) goto cleanup;
  112. file_size = ftell(fd);
  113. if (fseeko(fd, 0, SEEK_SET) != 0) goto cleanup;
  114. if (file_size < 0) goto cleanup;
  115. // init hash context
  116. for (int i = 0; i < hash_type_total; i++) {
  117. if (ctx[i]) ctx_init[i](ctx[i]);
  118. }
  119. // read stream and calculate checksum in a single loop
  120. // 'dispatch_io' has better performance, I will rewrite it later...
  121. if (block) {
  122. while (!done && !stop) {
  123. size_t size = fread(buf, 1, BUF_SIZE, fd);
  124. if (size < BUF_SIZE) {
  125. if (feof(fd)) done = YES; // finish
  126. else { stop = YES; break; } // error
  127. }
  128. for (int i = 0; i < hash_type_total; i++) {
  129. if (ctx[i]) ctx_update[i](ctx[i], buf, (CC_LONG)size);
  130. }
  131. readed += size;
  132. if (!done) {
  133. loop++;
  134. if ((loop % BLOCK_LOOP_FACTOR) == 0) {
  135. block(file_size, readed, &stop);
  136. }
  137. }
  138. }
  139. } else {
  140. while (!done && !stop) {
  141. size_t size = fread(buf, 1, BUF_SIZE_NO_PROGRESS, fd);
  142. if (size < BUF_SIZE_NO_PROGRESS) {
  143. if (feof(fd)) done = YES; // finish
  144. else { stop = YES; break; } // error
  145. }
  146. for (int i = 0; i < hash_type_total; i++) {
  147. if (ctx[i]) ctx_update[i](ctx[i], buf, (CC_LONG)size);
  148. }
  149. readed += size;
  150. }
  151. }
  152. // collect result
  153. if (done && !stop) {
  154. hash = [YYFileHash new];
  155. hash->_types = types;
  156. for (int i = 0; i < hash_type_total; i++) {
  157. if (ctx[i]) {
  158. ctx_final[i](digest[i], ctx[i]);
  159. NSUInteger type = 1 << i;
  160. NSData *data = [NSData dataWithBytes:digest[i] length:digist_length[i]];
  161. NSMutableString *str = [NSMutableString string];
  162. unsigned char *bytes = (unsigned char *)data.bytes;
  163. for (NSUInteger d = 0; d < data.length; d++) {
  164. [str appendFormat:@"%02x", bytes[d]];
  165. }
  166. switch (type) {
  167. case YYFileHashTypeMD2: {
  168. hash->_md2Data = data;
  169. hash->_md2String = str;
  170. } break;
  171. case YYFileHashTypeMD4: {
  172. hash->_md4Data = data;
  173. hash->_md4String = str;
  174. } break;
  175. case YYFileHashTypeMD5: {
  176. hash->_md5Data = data;
  177. hash->_md5String = str;
  178. } break;
  179. case YYFileHashTypeSHA1: {
  180. hash->_sha1Data = data;
  181. hash->_sha1String = str;
  182. } break;
  183. case YYFileHashTypeSHA224: {
  184. hash->_sha224Data = data;
  185. hash->_sha224String = str;
  186. } break;
  187. case YYFileHashTypeSHA256: {
  188. hash->_sha256Data = data;
  189. hash->_sha256String = str;
  190. } break;
  191. case YYFileHashTypeSHA384: {
  192. hash->_sha384Data = data;
  193. hash->_sha384String = str;
  194. } break;
  195. case YYFileHashTypeSHA512: {
  196. hash->_sha512Data = data;
  197. hash->_sha512String = str;
  198. } break;
  199. case YYFileHashTypeCRC32: {
  200. uint32_t hash32 = *((uint32_t *)bytes);
  201. hash->_crc32 = hash32;
  202. hash->_crc32String = [[NSString alloc] initWithFormat:@"%08x", hash32];
  203. } break;
  204. case YYFileHashTypeAdler32: {
  205. uint32_t hash32 = *((uint32_t *)bytes);
  206. hash->_adler32 = hash32;
  207. hash->_adler32String = [[NSString alloc] initWithFormat:@"%08x", hash32];
  208. } break;
  209. default:
  210. break;
  211. }
  212. }
  213. }
  214. }
  215. cleanup: // do cleanup when canceled of finished
  216. if (buf) free(buf);
  217. if (fd) fclose(fd);
  218. for (int i = 0; i < hash_type_total; i++) {
  219. if (ctx[i]) free(ctx[i]);
  220. if (digest[i]) free(digest[i]);
  221. }
  222. return hash;
  223. }
  224. #pragma mark - crc32 callback
  225. static int crc32_init(uLong *c) {
  226. *c = crc32(0L, Z_NULL, 0);
  227. return 0;
  228. }
  229. static int crc32_update(uLong *c, const void *data, CC_LONG len) {
  230. *c = crc32(*c, data, len);
  231. return 0;
  232. }
  233. static int crc32_final(unsigned char *buf, uLong *c) {
  234. *((uint32_t *)buf) = (uint32_t)*c;
  235. return 0;
  236. }
  237. #pragma mark - adler32 callback
  238. static int adler32_init(uLong *c) {
  239. *c = adler32(0L, Z_NULL, 0);
  240. return 0;
  241. }
  242. static int adler32_update(uLong *c, const void *data, CC_LONG len) {
  243. *c = adler32(*c, data, len);
  244. return 0;
  245. }
  246. static int adler32_final(unsigned char *buf, uLong *c) {
  247. *((uint32_t *)buf) = (uint32_t)*c;
  248. return 0;
  249. }
  250. @end