/core/externals/update-engine/externals/google-toolbox-for-mac/Foundation/GTMNSData+zlib.m

http://macfuse.googlecode.com/ · Objective C · 353 lines · 252 code · 54 blank · 47 comment · 40 complexity · ac1d999512129345c8d57cc77655aa6d MD5 · raw file

  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. typedef enum {
  23. CompressionModeZlib,
  24. CompressionModeGzip,
  25. CompressionModeRaw,
  26. } CompressionMode;
  27. @interface NSData (GTMZlibAdditionsPrivate)
  28. + (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
  29. length:(NSUInteger)length
  30. compressionLevel:(int)level
  31. mode:(CompressionMode)mode;
  32. + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
  33. length:(NSUInteger)length
  34. isRawData:(BOOL)isRawData;
  35. @end
  36. @implementation NSData (GTMZlibAdditionsPrivate)
  37. + (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
  38. length:(NSUInteger)length
  39. compressionLevel:(int)level
  40. mode:(CompressionMode)mode {
  41. if (!bytes || !length) {
  42. return nil;
  43. }
  44. #if defined(__LP64__) && __LP64__
  45. // Don't support > 32bit length for 64 bit, see note in header.
  46. if (length > UINT_MAX) {
  47. return nil;
  48. }
  49. #endif
  50. if (level == Z_DEFAULT_COMPRESSION) {
  51. // the default value is actually outside the range, so we have to let it
  52. // through specifically.
  53. } else if (level < Z_BEST_SPEED) {
  54. level = Z_BEST_SPEED;
  55. } else if (level > Z_BEST_COMPRESSION) {
  56. level = Z_BEST_COMPRESSION;
  57. }
  58. z_stream strm;
  59. bzero(&strm, sizeof(z_stream));
  60. int memLevel = 8; // the default
  61. int windowBits = 15; // the default
  62. switch (mode) {
  63. case CompressionModeZlib:
  64. // nothing to do
  65. break;
  66. case CompressionModeGzip:
  67. windowBits += 16; // enable gzip header instead of zlib header
  68. break;
  69. case CompressionModeRaw:
  70. windowBits *= -1; // Negative to mean no header.
  71. break;
  72. }
  73. int retCode;
  74. if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits,
  75. memLevel, Z_DEFAULT_STRATEGY)) != Z_OK) {
  76. // COV_NF_START - no real way to force this in a unittest (we guard all args)
  77. _GTMDevLog(@"Failed to init for deflate w/ level %d, error %d",
  78. level, retCode);
  79. return nil;
  80. // COV_NF_END
  81. }
  82. // hint the size at 1/4 the input size
  83. NSMutableData *result = [NSMutableData dataWithCapacity:(length/4)];
  84. unsigned char output[kChunkSize];
  85. // setup the input
  86. strm.avail_in = (unsigned int)length;
  87. strm.next_in = (unsigned char*)bytes;
  88. // loop to collect the data
  89. do {
  90. // update what we're passing in
  91. strm.avail_out = kChunkSize;
  92. strm.next_out = output;
  93. retCode = deflate(&strm, Z_FINISH);
  94. if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
  95. // COV_NF_START - no real way to force this in a unittest
  96. // (in inflate, we can feed bogus/truncated data to test, but an error
  97. // here would be some internal issue w/in zlib, and there isn't any real
  98. // way to test it)
  99. _GTMDevLog(@"Error trying to deflate some of the payload, error %d",
  100. retCode);
  101. deflateEnd(&strm);
  102. return nil;
  103. // COV_NF_END
  104. }
  105. // collect what we got
  106. unsigned gotBack = kChunkSize - strm.avail_out;
  107. if (gotBack > 0) {
  108. [result appendBytes:output length:gotBack];
  109. }
  110. } while (retCode == Z_OK);
  111. // if the loop exits, we used all input and the stream ended
  112. _GTMDevAssert(strm.avail_in == 0,
  113. @"thought we finished deflate w/o using all input, %u bytes left",
  114. strm.avail_in);
  115. _GTMDevAssert(retCode == Z_STREAM_END,
  116. @"thought we finished deflate w/o getting a result of stream end, code %d",
  117. retCode);
  118. // clean up
  119. deflateEnd(&strm);
  120. return result;
  121. } // gtm_dataByCompressingBytes:length:compressionLevel:useGzip:
  122. + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
  123. length:(NSUInteger)length
  124. isRawData:(BOOL)isRawData {
  125. if (!bytes || !length) {
  126. return nil;
  127. }
  128. #if defined(__LP64__) && __LP64__
  129. // Don't support > 32bit length for 64 bit, see note in header.
  130. if (length > UINT_MAX) {
  131. return nil;
  132. }
  133. #endif
  134. z_stream strm;
  135. bzero(&strm, sizeof(z_stream));
  136. // setup the input
  137. strm.avail_in = (unsigned int)length;
  138. strm.next_in = (unsigned char*)bytes;
  139. int windowBits = 15; // 15 to enable any window size
  140. if (isRawData) {
  141. windowBits *= -1; // make it negative to signal no header.
  142. } else {
  143. windowBits += 32; // and +32 to enable zlib or gzip header detection.
  144. }
  145. int retCode;
  146. if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
  147. // COV_NF_START - no real way to force this in a unittest (we guard all args)
  148. _GTMDevLog(@"Failed to init for inflate, error %d", retCode);
  149. return nil;
  150. // COV_NF_END
  151. }
  152. // hint the size at 4x the input size
  153. NSMutableData *result = [NSMutableData dataWithCapacity:(length*4)];
  154. unsigned char output[kChunkSize];
  155. // loop to collect the data
  156. do {
  157. // update what we're passing in
  158. strm.avail_out = kChunkSize;
  159. strm.next_out = output;
  160. retCode = inflate(&strm, Z_NO_FLUSH);
  161. if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
  162. _GTMDevLog(@"Error trying to inflate some of the payload, error %d: %s",
  163. retCode, strm.msg);
  164. inflateEnd(&strm);
  165. return nil;
  166. }
  167. // collect what we got
  168. unsigned gotBack = kChunkSize - strm.avail_out;
  169. if (gotBack > 0) {
  170. [result appendBytes:output length:gotBack];
  171. }
  172. } while (retCode == Z_OK);
  173. // make sure there wasn't more data tacked onto the end of a valid compressed
  174. // stream.
  175. if (strm.avail_in != 0) {
  176. _GTMDevLog(@"thought we finished inflate w/o using all input, %u bytes left",
  177. strm.avail_in);
  178. result = nil;
  179. }
  180. // the only way out of the loop was by hitting the end of the stream
  181. _GTMDevAssert(retCode == Z_STREAM_END,
  182. @"thought we finished inflate w/o getting a result of stream end, code %d",
  183. retCode);
  184. // clean up
  185. inflateEnd(&strm);
  186. return result;
  187. } // gtm_dataByInflatingBytes:length:windowBits:
  188. @end
  189. @implementation NSData (GTMZLibAdditions)
  190. + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
  191. length:(NSUInteger)length {
  192. return [self gtm_dataByCompressingBytes:bytes
  193. length:length
  194. compressionLevel:Z_DEFAULT_COMPRESSION
  195. mode:CompressionModeGzip];
  196. } // gtm_dataByGzippingBytes:length:
  197. + (NSData *)gtm_dataByGzippingData:(NSData *)data {
  198. return [self gtm_dataByCompressingBytes:[data bytes]
  199. length:[data length]
  200. compressionLevel:Z_DEFAULT_COMPRESSION
  201. mode:CompressionModeGzip];
  202. } // gtm_dataByGzippingData:
  203. + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
  204. length:(NSUInteger)length
  205. compressionLevel:(int)level {
  206. return [self gtm_dataByCompressingBytes:bytes
  207. length:length
  208. compressionLevel:level
  209. mode:CompressionModeGzip];
  210. } // gtm_dataByGzippingBytes:length:level:
  211. + (NSData *)gtm_dataByGzippingData:(NSData *)data
  212. compressionLevel:(int)level {
  213. return [self gtm_dataByCompressingBytes:[data bytes]
  214. length:[data length]
  215. compressionLevel:level
  216. mode:CompressionModeGzip];
  217. } // gtm_dataByGzippingData:level:
  218. #pragma mark -
  219. + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
  220. length:(NSUInteger)length {
  221. return [self gtm_dataByCompressingBytes:bytes
  222. length:length
  223. compressionLevel:Z_DEFAULT_COMPRESSION
  224. mode:CompressionModeZlib];
  225. } // gtm_dataByDeflatingBytes:length:
  226. + (NSData *)gtm_dataByDeflatingData:(NSData *)data {
  227. return [self gtm_dataByCompressingBytes:[data bytes]
  228. length:[data length]
  229. compressionLevel:Z_DEFAULT_COMPRESSION
  230. mode:CompressionModeZlib];
  231. } // gtm_dataByDeflatingData:
  232. + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
  233. length:(NSUInteger)length
  234. compressionLevel:(int)level {
  235. return [self gtm_dataByCompressingBytes:bytes
  236. length:length
  237. compressionLevel:level
  238. mode:CompressionModeZlib];
  239. } // gtm_dataByDeflatingBytes:length:level:
  240. + (NSData *)gtm_dataByDeflatingData:(NSData *)data
  241. compressionLevel:(int)level {
  242. return [self gtm_dataByCompressingBytes:[data bytes]
  243. length:[data length]
  244. compressionLevel:level
  245. mode:CompressionModeZlib];
  246. } // gtm_dataByDeflatingData:level:
  247. #pragma mark -
  248. + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
  249. length:(NSUInteger)length {
  250. return [self gtm_dataByInflatingBytes:bytes
  251. length:length
  252. isRawData:NO];
  253. } // gtm_dataByInflatingBytes:length:
  254. + (NSData *)gtm_dataByInflatingData:(NSData *)data {
  255. return [self gtm_dataByInflatingBytes:[data bytes]
  256. length:[data length]
  257. isRawData:NO];
  258. } // gtm_dataByInflatingData:
  259. #pragma mark -
  260. + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
  261. length:(NSUInteger)length {
  262. return [self gtm_dataByCompressingBytes:bytes
  263. length:length
  264. compressionLevel:Z_DEFAULT_COMPRESSION
  265. mode:CompressionModeRaw];
  266. } // gtm_dataByRawDeflatingBytes:length:
  267. + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data {
  268. return [self gtm_dataByCompressingBytes:[data bytes]
  269. length:[data length]
  270. compressionLevel:Z_DEFAULT_COMPRESSION
  271. mode:CompressionModeRaw];
  272. } // gtm_dataByRawDeflatingData:
  273. + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
  274. length:(NSUInteger)length
  275. compressionLevel:(int)level {
  276. return [self gtm_dataByCompressingBytes:bytes
  277. length:length
  278. compressionLevel:level
  279. mode:CompressionModeRaw];
  280. } // gtm_dataByRawDeflatingBytes:length:compressionLevel:
  281. + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
  282. compressionLevel:(int)level {
  283. return [self gtm_dataByCompressingBytes:[data bytes]
  284. length:[data length]
  285. compressionLevel:level
  286. mode:CompressionModeRaw];
  287. } // gtm_dataByRawDeflatingData:compressionLevel:
  288. + (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
  289. length:(NSUInteger)length {
  290. return [self gtm_dataByInflatingBytes:bytes
  291. length:length
  292. isRawData:YES];
  293. } // gtm_dataByRawInflatingBytes:length:
  294. + (NSData *)gtm_dataByRawInflatingData:(NSData *)data {
  295. return [self gtm_dataByInflatingBytes:[data bytes]
  296. length:[data length]
  297. isRawData:YES];
  298. } // gtm_dataByRawInflatingData:
  299. @end