// // GZIP.m // // Version 1.2.1 // // Created by Nick Lockwood on 03/06/2012. // Copyright (C) 2012 Charcoal Design // // Distributed under the permissive zlib License // Get the latest version from here: // // https://github.com/nicklockwood/GZIP // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source distribution. // #import "NSData+GZip.h" #import #pragma clang diagnostic ignored "-Wcast-qual" @implementation NSData (GZIP) - (NSData *)gzippedDataWithCompressionLevel:(float)level { if (self.length == 0 || [self isGzippedData]) { return self; } z_stream stream; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL; stream.avail_in = (uint)self.length; stream.next_in = (Bytef *)(void *)self.bytes; stream.total_out = 0; stream.avail_out = 0; static const NSUInteger ChunkSize = 16384; NSMutableData *output = nil; int compression = (level < 0.0f)? Z_DEFAULT_COMPRESSION: (int)(roundf(level * 9)); if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK) { output = [NSMutableData dataWithLength:ChunkSize]; while (stream.avail_out == 0) { if (stream.total_out >= output.length) { output.length += ChunkSize; } stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out; stream.avail_out = (uInt)(output.length - stream.total_out); deflate(&stream, Z_FINISH); } deflateEnd(&stream); output.length = stream.total_out; } return output; } - (NSData *)gzippedData { return [self gzippedDataWithCompressionLevel:-1.0f]; } - (NSData *)gunzippedData { if (self.length == 0 || ![self isGzippedData]) { return self; } z_stream stream; stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.avail_in = (uint)self.length; stream.next_in = (Bytef *)self.bytes; stream.total_out = 0; stream.avail_out = 0; NSMutableData *output = nil; if (inflateInit2(&stream, 47) == Z_OK) { int status = Z_OK; output = [NSMutableData dataWithCapacity:self.length * 2]; while (status == Z_OK) { if (stream.total_out >= output.length) { output.length += self.length / 2; } stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out; stream.avail_out = (uInt)(output.length - stream.total_out); status = inflate (&stream, Z_SYNC_FLUSH); } if (inflateEnd(&stream) == Z_OK) { if (status == Z_STREAM_END) { output.length = stream.total_out; } } } return output; } - (BOOL)isGzippedData { const UInt8 *bytes = (const UInt8 *)self.bytes; return (self.length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b); } @end