You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

531 lines
20 KiB

6 years ago
  1. //
  2. // GTMNSData+zlib.m
  3. //
  4. // Copyright 2007-2008 Google Inc.
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7. // use this file except in compliance with the License. You may obtain a copy
  8. // of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  14. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  15. // License for the specific language governing permissions and limitations under
  16. // the License.
  17. //
  18. #import "GTMNSData+zlib.h"
  19. #import <zlib.h>
  20. #import "GTMDefines.h"
  21. #define kChunkSize 1024
  22. NSString *const GTMNSDataZlibErrorDomain = @"com.google.GTMNSDataZlibErrorDomain";
  23. NSString *const GTMNSDataZlibErrorKey = @"GTMNSDataZlibErrorKey";
  24. NSString *const GTMNSDataZlibRemainingBytesKey = @"GTMNSDataZlibRemainingBytesKey";
  25. typedef enum {
  26. CompressionModeZlib,
  27. CompressionModeGzip,
  28. CompressionModeRaw,
  29. } CompressionMode;
  30. @interface NSData (GTMZlibAdditionsPrivate)
  31. + (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
  32. length:(NSUInteger)length
  33. compressionLevel:(int)level
  34. mode:(CompressionMode)mode
  35. error:(NSError **)error;
  36. + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
  37. length:(NSUInteger)length
  38. isRawData:(BOOL)isRawData
  39. error:(NSError **)error;
  40. @end
  41. @implementation NSData (GTMZlibAdditionsPrivate)
  42. + (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
  43. length:(NSUInteger)length
  44. compressionLevel:(int)level
  45. mode:(CompressionMode)mode
  46. error:(NSError **)error {
  47. if (!bytes || !length) {
  48. return nil;
  49. }
  50. #if defined(__LP64__) && __LP64__
  51. // Don't support > 32bit length for 64 bit, see note in header.
  52. if (length > UINT_MAX) {
  53. if (error) {
  54. *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
  55. code:GTMNSDataZlibErrorGreaterThan32BitsToCompress
  56. userInfo:nil];
  57. }
  58. return nil;
  59. }
  60. #endif
  61. if (level == Z_DEFAULT_COMPRESSION) {
  62. // the default value is actually outside the range, so we have to let it
  63. // through specifically.
  64. } else if (level < Z_BEST_SPEED) {
  65. level = Z_BEST_SPEED;
  66. } else if (level > Z_BEST_COMPRESSION) {
  67. level = Z_BEST_COMPRESSION;
  68. }
  69. z_stream strm;
  70. bzero(&strm, sizeof(z_stream));
  71. int memLevel = 8; // the default
  72. int windowBits = 15; // the default
  73. switch (mode) {
  74. case CompressionModeZlib:
  75. // nothing to do
  76. break;
  77. case CompressionModeGzip:
  78. windowBits += 16; // enable gzip header instead of zlib header
  79. break;
  80. case CompressionModeRaw:
  81. windowBits *= -1; // Negative to mean no header.
  82. break;
  83. }
  84. int retCode;
  85. if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits,
  86. memLevel, Z_DEFAULT_STRATEGY)) != Z_OK) {
  87. // COV_NF_START - no real way to force this in a unittest (we guard all args)
  88. if (error) {
  89. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
  90. forKey:GTMNSDataZlibErrorKey];
  91. *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
  92. code:GTMNSDataZlibErrorInternal
  93. userInfo:userInfo];
  94. }
  95. return nil;
  96. // COV_NF_END
  97. }
  98. // hint the size at 1/4 the input size
  99. NSMutableData *result = [NSMutableData dataWithCapacity:(length/4)];
  100. unsigned char output[kChunkSize];
  101. // setup the input
  102. strm.avail_in = (unsigned int)length;
  103. strm.next_in = (unsigned char*)bytes;
  104. // loop to collect the data
  105. do {
  106. // update what we're passing in
  107. strm.avail_out = kChunkSize;
  108. strm.next_out = output;
  109. retCode = deflate(&strm, Z_FINISH);
  110. if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
  111. // COV_NF_START - no real way to force this in a unittest
  112. // (in inflate, we can feed bogus/truncated data to test, but an error
  113. // here would be some internal issue w/in zlib, and there isn't any real
  114. // way to test it)
  115. if (error) {
  116. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
  117. forKey:GTMNSDataZlibErrorKey];
  118. *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
  119. code:GTMNSDataZlibErrorInternal
  120. userInfo:userInfo];
  121. }
  122. deflateEnd(&strm);
  123. return nil;
  124. // COV_NF_END
  125. }
  126. // collect what we got
  127. unsigned gotBack = kChunkSize - strm.avail_out;
  128. if (gotBack > 0) {
  129. [result appendBytes:output length:gotBack];
  130. }
  131. } while (retCode == Z_OK);
  132. // if the loop exits, we used all input and the stream ended
  133. _GTMDevAssert(strm.avail_in == 0,
  134. @"thought we finished deflate w/o using all input, %u bytes left",
  135. strm.avail_in);
  136. _GTMDevAssert(retCode == Z_STREAM_END,
  137. @"thought we finished deflate w/o getting a result of stream end, code %d",
  138. retCode);
  139. // clean up
  140. deflateEnd(&strm);
  141. return result;
  142. } // gtm_dataByCompressingBytes:length:compressionLevel:useGzip:
  143. + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
  144. length:(NSUInteger)length
  145. isRawData:(BOOL)isRawData
  146. error:(NSError **)error {
  147. if (!bytes || !length) {
  148. return nil;
  149. }
  150. #if defined(__LP64__) && __LP64__
  151. // Don't support > 32bit length for 64 bit, see note in header.
  152. if (length > UINT_MAX) {
  153. return nil;
  154. }
  155. #endif
  156. z_stream strm;
  157. bzero(&strm, sizeof(z_stream));
  158. // setup the input
  159. strm.avail_in = (unsigned int)length;
  160. strm.next_in = (unsigned char*)bytes;
  161. int windowBits = 15; // 15 to enable any window size
  162. if (isRawData) {
  163. windowBits *= -1; // make it negative to signal no header.
  164. } else {
  165. windowBits += 32; // and +32 to enable zlib or gzip header detection.
  166. }
  167. int retCode;
  168. if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
  169. // COV_NF_START - no real way to force this in a unittest (we guard all args)
  170. if (error) {
  171. NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
  172. forKey:GTMNSDataZlibErrorKey];
  173. *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
  174. code:GTMNSDataZlibErrorInternal
  175. userInfo:userInfo];
  176. }
  177. return nil;
  178. // COV_NF_END
  179. }
  180. // hint the size at 4x the input size
  181. NSMutableData *result = [NSMutableData dataWithCapacity:(length*4)];
  182. unsigned char output[kChunkSize];
  183. // loop to collect the data
  184. do {
  185. // update what we're passing in
  186. strm.avail_out = kChunkSize;
  187. strm.next_out = output;
  188. retCode = inflate(&strm, Z_NO_FLUSH);
  189. if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
  190. if (error) {
  191. NSMutableDictionary *userInfo =
  192. [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
  193. forKey:GTMNSDataZlibErrorKey];
  194. if (strm.msg) {
  195. NSString *message = [NSString stringWithUTF8String:strm.msg];
  196. if (message) {
  197. [userInfo setObject:message forKey:NSLocalizedDescriptionKey];
  198. }
  199. }
  200. *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
  201. code:GTMNSDataZlibErrorInternal
  202. userInfo:userInfo];
  203. }
  204. inflateEnd(&strm);
  205. return nil;
  206. }
  207. // collect what we got
  208. unsigned gotBack = kChunkSize - strm.avail_out;
  209. if (gotBack > 0) {
  210. [result appendBytes:output length:gotBack];
  211. }
  212. } while (retCode == Z_OK);
  213. // make sure there wasn't more data tacked onto the end of a valid compressed
  214. // stream.
  215. if (strm.avail_in != 0) {
  216. if (error) {
  217. NSDictionary *userInfo =
  218. [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:strm.avail_in]
  219. forKey:GTMNSDataZlibRemainingBytesKey];
  220. *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
  221. code:GTMNSDataZlibErrorDataRemaining
  222. userInfo:userInfo];
  223. }
  224. result = nil;
  225. }
  226. // the only way out of the loop was by hitting the end of the stream
  227. _GTMDevAssert(retCode == Z_STREAM_END,
  228. @"thought we finished inflate w/o getting a result of stream end, code %d",
  229. retCode);
  230. // clean up
  231. inflateEnd(&strm);
  232. return result;
  233. } // gtm_dataByInflatingBytes:length:windowBits:
  234. @end
  235. @implementation NSData (GTMZLibAdditions)
  236. + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
  237. length:(NSUInteger)length {
  238. return [self gtm_dataByGzippingBytes:bytes length:length error:NULL];
  239. } // gtm_dataByGzippingBytes:length:
  240. + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
  241. length:(NSUInteger)length
  242. error:(NSError **)error {
  243. return [self gtm_dataByCompressingBytes:bytes
  244. length:length
  245. compressionLevel:Z_DEFAULT_COMPRESSION
  246. mode:CompressionModeGzip
  247. error:error];
  248. } // gtm_dataByGzippingBytes:length:error:
  249. + (NSData *)gtm_dataByGzippingData:(NSData *)data {
  250. return [self gtm_dataByGzippingData:data error:NULL];
  251. } // gtm_dataByGzippingData:
  252. + (NSData *)gtm_dataByGzippingData:(NSData *)data error:(NSError **)error {
  253. return [self gtm_dataByCompressingBytes:[data bytes]
  254. length:[data length]
  255. compressionLevel:Z_DEFAULT_COMPRESSION
  256. mode:CompressionModeGzip
  257. error:error];
  258. } // gtm_dataByGzippingData:error:
  259. + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
  260. length:(NSUInteger)length
  261. compressionLevel:(int)level {
  262. return [self gtm_dataByGzippingBytes:bytes
  263. length:length
  264. compressionLevel:level
  265. error:NULL];
  266. } // gtm_dataByGzippingBytes:length:level:
  267. + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
  268. length:(NSUInteger)length
  269. compressionLevel:(int)level
  270. error:(NSError **)error{
  271. return [self gtm_dataByCompressingBytes:bytes
  272. length:length
  273. compressionLevel:level
  274. mode:CompressionModeGzip
  275. error:error];
  276. } // gtm_dataByGzippingBytes:length:level:error
  277. + (NSData *)gtm_dataByGzippingData:(NSData *)data
  278. compressionLevel:(int)level {
  279. return [self gtm_dataByGzippingData:data
  280. compressionLevel:level
  281. error:NULL];
  282. } // gtm_dataByGzippingData:level:
  283. + (NSData *)gtm_dataByGzippingData:(NSData *)data
  284. compressionLevel:(int)level
  285. error:(NSError **)error{
  286. return [self gtm_dataByCompressingBytes:[data bytes]
  287. length:[data length]
  288. compressionLevel:level
  289. mode:CompressionModeGzip
  290. error:error];
  291. } // gtm_dataByGzippingData:level:error
  292. #pragma mark -
  293. + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
  294. length:(NSUInteger)length {
  295. return [self gtm_dataByDeflatingBytes:bytes
  296. length:length
  297. error:NULL];
  298. } // gtm_dataByDeflatingBytes:length:
  299. + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
  300. length:(NSUInteger)length
  301. error:(NSError **)error{
  302. return [self gtm_dataByCompressingBytes:bytes
  303. length:length
  304. compressionLevel:Z_DEFAULT_COMPRESSION
  305. mode:CompressionModeZlib
  306. error:error];
  307. } // gtm_dataByDeflatingBytes:length:error
  308. + (NSData *)gtm_dataByDeflatingData:(NSData *)data {
  309. return [self gtm_dataByDeflatingData:data error:NULL];
  310. } // gtm_dataByDeflatingData:
  311. + (NSData *)gtm_dataByDeflatingData:(NSData *)data error:(NSError **)error {
  312. return [self gtm_dataByCompressingBytes:[data bytes]
  313. length:[data length]
  314. compressionLevel:Z_DEFAULT_COMPRESSION
  315. mode:CompressionModeZlib
  316. error:error];
  317. } // gtm_dataByDeflatingData:
  318. + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
  319. length:(NSUInteger)length
  320. compressionLevel:(int)level {
  321. return [self gtm_dataByDeflatingBytes:bytes
  322. length:length
  323. compressionLevel:level
  324. error:NULL];
  325. } // gtm_dataByDeflatingBytes:length:level:
  326. + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
  327. length:(NSUInteger)length
  328. compressionLevel:(int)level
  329. error:(NSError **)error {
  330. return [self gtm_dataByCompressingBytes:bytes
  331. length:length
  332. compressionLevel:level
  333. mode:CompressionModeZlib
  334. error:error];
  335. } // gtm_dataByDeflatingBytes:length:level:error:
  336. + (NSData *)gtm_dataByDeflatingData:(NSData *)data
  337. compressionLevel:(int)level {
  338. return [self gtm_dataByDeflatingData:data
  339. compressionLevel:level
  340. error:NULL];
  341. } // gtm_dataByDeflatingData:level:
  342. + (NSData *)gtm_dataByDeflatingData:(NSData *)data
  343. compressionLevel:(int)level
  344. error:(NSError **)error {
  345. return [self gtm_dataByCompressingBytes:[data bytes]
  346. length:[data length]
  347. compressionLevel:level
  348. mode:CompressionModeZlib
  349. error:error];
  350. } // gtm_dataByDeflatingData:level:error:
  351. #pragma mark -
  352. + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
  353. length:(NSUInteger)length {
  354. return [self gtm_dataByInflatingBytes:bytes
  355. length:length
  356. error:NULL];
  357. } // gtm_dataByInflatingBytes:length:
  358. + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
  359. length:(NSUInteger)length
  360. error:(NSError **)error {
  361. return [self gtm_dataByInflatingBytes:bytes
  362. length:length
  363. isRawData:NO
  364. error:error];
  365. } // gtm_dataByInflatingBytes:length:error:
  366. + (NSData *)gtm_dataByInflatingData:(NSData *)data {
  367. return [self gtm_dataByInflatingData:data error:NULL];
  368. } // gtm_dataByInflatingData:
  369. + (NSData *)gtm_dataByInflatingData:(NSData *)data
  370. error:(NSError **)error {
  371. return [self gtm_dataByInflatingBytes:[data bytes]
  372. length:[data length]
  373. isRawData:NO
  374. error:error];
  375. } // gtm_dataByInflatingData:
  376. #pragma mark -
  377. + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
  378. length:(NSUInteger)length {
  379. return [self gtm_dataByRawDeflatingBytes:(const void *)bytes
  380. length:(NSUInteger)length
  381. error:NULL];
  382. } // gtm_dataByRawDeflatingBytes:length:
  383. + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
  384. length:(NSUInteger)length
  385. error:(NSError **)error {
  386. return [self gtm_dataByCompressingBytes:bytes
  387. length:length
  388. compressionLevel:Z_DEFAULT_COMPRESSION
  389. mode:CompressionModeRaw
  390. error:error];
  391. } // gtm_dataByRawDeflatingBytes:length:error:
  392. + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data {
  393. return [self gtm_dataByRawDeflatingData:data error:NULL];
  394. } // gtm_dataByRawDeflatingData:
  395. + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data error:(NSError **)error {
  396. return [self gtm_dataByCompressingBytes:[data bytes]
  397. length:[data length]
  398. compressionLevel:Z_DEFAULT_COMPRESSION
  399. mode:CompressionModeRaw
  400. error:error];
  401. } // gtm_dataByRawDeflatingData:error:
  402. + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
  403. length:(NSUInteger)length
  404. compressionLevel:(int)level {
  405. return [self gtm_dataByRawDeflatingBytes:bytes
  406. length:length
  407. compressionLevel:level
  408. error:NULL];
  409. } // gtm_dataByRawDeflatingBytes:length:compressionLevel:
  410. + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
  411. length:(NSUInteger)length
  412. compressionLevel:(int)level
  413. error:(NSError **)error{
  414. return [self gtm_dataByCompressingBytes:bytes
  415. length:length
  416. compressionLevel:level
  417. mode:CompressionModeRaw
  418. error:error];
  419. } // gtm_dataByRawDeflatingBytes:length:compressionLevel:error:
  420. + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
  421. compressionLevel:(int)level {
  422. return [self gtm_dataByRawDeflatingData:data
  423. compressionLevel:level
  424. error:NULL];
  425. } // gtm_dataByRawDeflatingData:compressionLevel:
  426. + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
  427. compressionLevel:(int)level
  428. error:(NSError **)error {
  429. return [self gtm_dataByCompressingBytes:[data bytes]
  430. length:[data length]
  431. compressionLevel:level
  432. mode:CompressionModeRaw
  433. error:error];
  434. } // gtm_dataByRawDeflatingData:compressionLevel:error:
  435. + (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
  436. length:(NSUInteger)length {
  437. return [self gtm_dataByInflatingBytes:bytes
  438. length:length
  439. error:NULL];
  440. } // gtm_dataByRawInflatingBytes:length:
  441. + (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
  442. length:(NSUInteger)length
  443. error:(NSError **)error{
  444. return [self gtm_dataByInflatingBytes:bytes
  445. length:length
  446. isRawData:YES
  447. error:error];
  448. } // gtm_dataByRawInflatingBytes:length:error:
  449. + (NSData *)gtm_dataByRawInflatingData:(NSData *)data {
  450. return [self gtm_dataByRawInflatingData:data
  451. error:NULL];
  452. } // gtm_dataByRawInflatingData:
  453. + (NSData *)gtm_dataByRawInflatingData:(NSData *)data
  454. error:(NSError **)error {
  455. return [self gtm_dataByInflatingBytes:[data bytes]
  456. length:[data length]
  457. isRawData:YES
  458. error:error];
  459. } // gtm_dataByRawInflatingData:error:
  460. @end