PageRenderTime 75ms CodeModel.GetById 7ms app.highlight 64ms RepoModel.GetById 1ms app.codeStats 0ms

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