| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531 |
- //
- // GTMNSData+zlib.m
- //
- // Copyright 2007-2008 Google Inc.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not
- // use this file except in compliance with the License. You may obtain a copy
- // of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- // License for the specific language governing permissions and limitations under
- // the License.
- //
- #import "GTMNSData+zlib.h"
- #import <zlib.h>
- #import "GTMDefines.h"
- #define kChunkSize 1024
- NSString *const GTMNSDataZlibErrorDomain = @"com.google.GTMNSDataZlibErrorDomain";
- NSString *const GTMNSDataZlibErrorKey = @"GTMNSDataZlibErrorKey";
- NSString *const GTMNSDataZlibRemainingBytesKey = @"GTMNSDataZlibRemainingBytesKey";
- typedef enum {
- CompressionModeZlib,
- CompressionModeGzip,
- CompressionModeRaw,
- } CompressionMode;
- @interface NSData (GTMZlibAdditionsPrivate)
- + (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level
- mode:(CompressionMode)mode
- error:(NSError **)error;
- + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- isRawData:(BOOL)isRawData
- error:(NSError **)error;
- @end
- @implementation NSData (GTMZlibAdditionsPrivate)
- + (NSData *)gtm_dataByCompressingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level
- mode:(CompressionMode)mode
- error:(NSError **)error {
- if (!bytes || !length) {
- return nil;
- }
- #if defined(__LP64__) && __LP64__
- // Don't support > 32bit length for 64 bit, see note in header.
- if (length > UINT_MAX) {
- if (error) {
- *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
- code:GTMNSDataZlibErrorGreaterThan32BitsToCompress
- userInfo:nil];
- }
- return nil;
- }
- #endif
- if (level == Z_DEFAULT_COMPRESSION) {
- // the default value is actually outside the range, so we have to let it
- // through specifically.
- } else if (level < Z_BEST_SPEED) {
- level = Z_BEST_SPEED;
- } else if (level > Z_BEST_COMPRESSION) {
- level = Z_BEST_COMPRESSION;
- }
- z_stream strm;
- bzero(&strm, sizeof(z_stream));
- int memLevel = 8; // the default
- int windowBits = 15; // the default
- switch (mode) {
- case CompressionModeZlib:
- // nothing to do
- break;
- case CompressionModeGzip:
- windowBits += 16; // enable gzip header instead of zlib header
- break;
- case CompressionModeRaw:
- windowBits *= -1; // Negative to mean no header.
- break;
- }
- int retCode;
- if ((retCode = deflateInit2(&strm, level, Z_DEFLATED, windowBits,
- memLevel, Z_DEFAULT_STRATEGY)) != Z_OK) {
- // COV_NF_START - no real way to force this in a unittest (we guard all args)
- if (error) {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
- forKey:GTMNSDataZlibErrorKey];
- *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
- code:GTMNSDataZlibErrorInternal
- userInfo:userInfo];
- }
- return nil;
- // COV_NF_END
- }
- // hint the size at 1/4 the input size
- NSMutableData *result = [NSMutableData dataWithCapacity:(length/4)];
- unsigned char output[kChunkSize];
- // setup the input
- strm.avail_in = (unsigned int)length;
- strm.next_in = (unsigned char*)bytes;
- // loop to collect the data
- do {
- // update what we're passing in
- strm.avail_out = kChunkSize;
- strm.next_out = output;
- retCode = deflate(&strm, Z_FINISH);
- if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
- // COV_NF_START - no real way to force this in a unittest
- // (in inflate, we can feed bogus/truncated data to test, but an error
- // here would be some internal issue w/in zlib, and there isn't any real
- // way to test it)
- if (error) {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
- forKey:GTMNSDataZlibErrorKey];
- *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
- code:GTMNSDataZlibErrorInternal
- userInfo:userInfo];
- }
- deflateEnd(&strm);
- return nil;
- // COV_NF_END
- }
- // collect what we got
- unsigned gotBack = kChunkSize - strm.avail_out;
- if (gotBack > 0) {
- [result appendBytes:output length:gotBack];
- }
- } while (retCode == Z_OK);
- // if the loop exits, we used all input and the stream ended
- _GTMDevAssert(strm.avail_in == 0,
- @"thought we finished deflate w/o using all input, %u bytes left",
- strm.avail_in);
- _GTMDevAssert(retCode == Z_STREAM_END,
- @"thought we finished deflate w/o getting a result of stream end, code %d",
- retCode);
- // clean up
- deflateEnd(&strm);
- return result;
- } // gtm_dataByCompressingBytes:length:compressionLevel:useGzip:
- + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- isRawData:(BOOL)isRawData
- error:(NSError **)error {
- if (!bytes || !length) {
- return nil;
- }
- #if defined(__LP64__) && __LP64__
- // Don't support > 32bit length for 64 bit, see note in header.
- if (length > UINT_MAX) {
- return nil;
- }
- #endif
- z_stream strm;
- bzero(&strm, sizeof(z_stream));
- // setup the input
- strm.avail_in = (unsigned int)length;
- strm.next_in = (unsigned char*)bytes;
- int windowBits = 15; // 15 to enable any window size
- if (isRawData) {
- windowBits *= -1; // make it negative to signal no header.
- } else {
- windowBits += 32; // and +32 to enable zlib or gzip header detection.
- }
- int retCode;
- if ((retCode = inflateInit2(&strm, windowBits)) != Z_OK) {
- // COV_NF_START - no real way to force this in a unittest (we guard all args)
- if (error) {
- NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
- forKey:GTMNSDataZlibErrorKey];
- *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
- code:GTMNSDataZlibErrorInternal
- userInfo:userInfo];
- }
- return nil;
- // COV_NF_END
- }
- // hint the size at 4x the input size
- NSMutableData *result = [NSMutableData dataWithCapacity:(length*4)];
- unsigned char output[kChunkSize];
- // loop to collect the data
- do {
- // update what we're passing in
- strm.avail_out = kChunkSize;
- strm.next_out = output;
- retCode = inflate(&strm, Z_NO_FLUSH);
- if ((retCode != Z_OK) && (retCode != Z_STREAM_END)) {
- if (error) {
- NSMutableDictionary *userInfo =
- [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:retCode]
- forKey:GTMNSDataZlibErrorKey];
- if (strm.msg) {
- NSString *message = [NSString stringWithUTF8String:strm.msg];
- if (message) {
- [userInfo setObject:message forKey:NSLocalizedDescriptionKey];
- }
- }
- *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
- code:GTMNSDataZlibErrorInternal
- userInfo:userInfo];
- }
- inflateEnd(&strm);
- return nil;
- }
- // collect what we got
- unsigned gotBack = kChunkSize - strm.avail_out;
- if (gotBack > 0) {
- [result appendBytes:output length:gotBack];
- }
- } while (retCode == Z_OK);
- // make sure there wasn't more data tacked onto the end of a valid compressed
- // stream.
- if (strm.avail_in != 0) {
- if (error) {
- NSDictionary *userInfo =
- [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:strm.avail_in]
- forKey:GTMNSDataZlibRemainingBytesKey];
- *error = [NSError errorWithDomain:GTMNSDataZlibErrorDomain
- code:GTMNSDataZlibErrorDataRemaining
- userInfo:userInfo];
- }
- result = nil;
- }
- // the only way out of the loop was by hitting the end of the stream
- _GTMDevAssert(retCode == Z_STREAM_END,
- @"thought we finished inflate w/o getting a result of stream end, code %d",
- retCode);
- // clean up
- inflateEnd(&strm);
- return result;
- } // gtm_dataByInflatingBytes:length:windowBits:
- @end
- @implementation NSData (GTMZLibAdditions)
- + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(NSUInteger)length {
- return [self gtm_dataByGzippingBytes:bytes length:length error:NULL];
- } // gtm_dataByGzippingBytes:length:
- + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(NSUInteger)length
- error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:bytes
- length:length
- compressionLevel:Z_DEFAULT_COMPRESSION
- mode:CompressionModeGzip
- error:error];
- } // gtm_dataByGzippingBytes:length:error:
- + (NSData *)gtm_dataByGzippingData:(NSData *)data {
- return [self gtm_dataByGzippingData:data error:NULL];
- } // gtm_dataByGzippingData:
- + (NSData *)gtm_dataByGzippingData:(NSData *)data error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:[data bytes]
- length:[data length]
- compressionLevel:Z_DEFAULT_COMPRESSION
- mode:CompressionModeGzip
- error:error];
- } // gtm_dataByGzippingData:error:
- + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level {
- return [self gtm_dataByGzippingBytes:bytes
- length:length
- compressionLevel:level
- error:NULL];
- } // gtm_dataByGzippingBytes:length:level:
- + (NSData *)gtm_dataByGzippingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level
- error:(NSError **)error{
- return [self gtm_dataByCompressingBytes:bytes
- length:length
- compressionLevel:level
- mode:CompressionModeGzip
- error:error];
- } // gtm_dataByGzippingBytes:length:level:error
- + (NSData *)gtm_dataByGzippingData:(NSData *)data
- compressionLevel:(int)level {
- return [self gtm_dataByGzippingData:data
- compressionLevel:level
- error:NULL];
- } // gtm_dataByGzippingData:level:
- + (NSData *)gtm_dataByGzippingData:(NSData *)data
- compressionLevel:(int)level
- error:(NSError **)error{
- return [self gtm_dataByCompressingBytes:[data bytes]
- length:[data length]
- compressionLevel:level
- mode:CompressionModeGzip
- error:error];
- } // gtm_dataByGzippingData:level:error
- #pragma mark -
- + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length {
- return [self gtm_dataByDeflatingBytes:bytes
- length:length
- error:NULL];
- } // gtm_dataByDeflatingBytes:length:
- + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- error:(NSError **)error{
- return [self gtm_dataByCompressingBytes:bytes
- length:length
- compressionLevel:Z_DEFAULT_COMPRESSION
- mode:CompressionModeZlib
- error:error];
- } // gtm_dataByDeflatingBytes:length:error
- + (NSData *)gtm_dataByDeflatingData:(NSData *)data {
- return [self gtm_dataByDeflatingData:data error:NULL];
- } // gtm_dataByDeflatingData:
- + (NSData *)gtm_dataByDeflatingData:(NSData *)data error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:[data bytes]
- length:[data length]
- compressionLevel:Z_DEFAULT_COMPRESSION
- mode:CompressionModeZlib
- error:error];
- } // gtm_dataByDeflatingData:
- + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level {
- return [self gtm_dataByDeflatingBytes:bytes
- length:length
- compressionLevel:level
- error:NULL];
- } // gtm_dataByDeflatingBytes:length:level:
- + (NSData *)gtm_dataByDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level
- error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:bytes
- length:length
- compressionLevel:level
- mode:CompressionModeZlib
- error:error];
- } // gtm_dataByDeflatingBytes:length:level:error:
- + (NSData *)gtm_dataByDeflatingData:(NSData *)data
- compressionLevel:(int)level {
- return [self gtm_dataByDeflatingData:data
- compressionLevel:level
- error:NULL];
- } // gtm_dataByDeflatingData:level:
- + (NSData *)gtm_dataByDeflatingData:(NSData *)data
- compressionLevel:(int)level
- error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:[data bytes]
- length:[data length]
- compressionLevel:level
- mode:CompressionModeZlib
- error:error];
- } // gtm_dataByDeflatingData:level:error:
- #pragma mark -
- + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
- length:(NSUInteger)length {
- return [self gtm_dataByInflatingBytes:bytes
- length:length
- error:NULL];
- } // gtm_dataByInflatingBytes:length:
- + (NSData *)gtm_dataByInflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- error:(NSError **)error {
- return [self gtm_dataByInflatingBytes:bytes
- length:length
- isRawData:NO
- error:error];
- } // gtm_dataByInflatingBytes:length:error:
- + (NSData *)gtm_dataByInflatingData:(NSData *)data {
- return [self gtm_dataByInflatingData:data error:NULL];
- } // gtm_dataByInflatingData:
- + (NSData *)gtm_dataByInflatingData:(NSData *)data
- error:(NSError **)error {
- return [self gtm_dataByInflatingBytes:[data bytes]
- length:[data length]
- isRawData:NO
- error:error];
- } // gtm_dataByInflatingData:
- #pragma mark -
- + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length {
- return [self gtm_dataByRawDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- error:NULL];
- } // gtm_dataByRawDeflatingBytes:length:
- + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:bytes
- length:length
- compressionLevel:Z_DEFAULT_COMPRESSION
- mode:CompressionModeRaw
- error:error];
- } // gtm_dataByRawDeflatingBytes:length:error:
- + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data {
- return [self gtm_dataByRawDeflatingData:data error:NULL];
- } // gtm_dataByRawDeflatingData:
- + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:[data bytes]
- length:[data length]
- compressionLevel:Z_DEFAULT_COMPRESSION
- mode:CompressionModeRaw
- error:error];
- } // gtm_dataByRawDeflatingData:error:
- + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level {
- return [self gtm_dataByRawDeflatingBytes:bytes
- length:length
- compressionLevel:level
- error:NULL];
- } // gtm_dataByRawDeflatingBytes:length:compressionLevel:
- + (NSData *)gtm_dataByRawDeflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- compressionLevel:(int)level
- error:(NSError **)error{
- return [self gtm_dataByCompressingBytes:bytes
- length:length
- compressionLevel:level
- mode:CompressionModeRaw
- error:error];
- } // gtm_dataByRawDeflatingBytes:length:compressionLevel:error:
- + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
- compressionLevel:(int)level {
- return [self gtm_dataByRawDeflatingData:data
- compressionLevel:level
- error:NULL];
- } // gtm_dataByRawDeflatingData:compressionLevel:
- + (NSData *)gtm_dataByRawDeflatingData:(NSData *)data
- compressionLevel:(int)level
- error:(NSError **)error {
- return [self gtm_dataByCompressingBytes:[data bytes]
- length:[data length]
- compressionLevel:level
- mode:CompressionModeRaw
- error:error];
- } // gtm_dataByRawDeflatingData:compressionLevel:error:
- + (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
- length:(NSUInteger)length {
- return [self gtm_dataByInflatingBytes:bytes
- length:length
- error:NULL];
- } // gtm_dataByRawInflatingBytes:length:
- + (NSData *)gtm_dataByRawInflatingBytes:(const void *)bytes
- length:(NSUInteger)length
- error:(NSError **)error{
- return [self gtm_dataByInflatingBytes:bytes
- length:length
- isRawData:YES
- error:error];
- } // gtm_dataByRawInflatingBytes:length:error:
- + (NSData *)gtm_dataByRawInflatingData:(NSData *)data {
- return [self gtm_dataByRawInflatingData:data
- error:NULL];
- } // gtm_dataByRawInflatingData:
- + (NSData *)gtm_dataByRawInflatingData:(NSData *)data
- error:(NSError **)error {
- return [self gtm_dataByInflatingBytes:[data bytes]
- length:[data length]
- isRawData:YES
- error:error];
- } // gtm_dataByRawInflatingData:error:
- @end
|