1/* 2 Copyright (C) 2000-2005 SKYRIX Software AG 3 4 This file is part of SOPE. 5 6 SOPE is free software; you can redistribute it and/or modify it under 7 the terms of the GNU Lesser General Public License as published by the 8 Free Software Foundation; either version 2, or (at your option) any 9 later version. 10 11 SOPE is distributed in the hope that it will be useful, but WITHOUT ANY 12 WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 14 License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with SOPE; see the file COPYING. If not, write to the 18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 19 02111-1307, USA. 20*/ 21 22#include "NSData+gzip.h" 23#include "common.h" 24 25#ifdef Assert 26# undef Assert 27#endif 28 29#include <zlib.h> 30#ifndef DEF_MEM_LEVEL /* zutil.h */ 31# if MAX_MEM_LEVEL >= 8 32# define DEF_MEM_LEVEL 8 33# else 34# define DEF_MEM_LEVEL MAX_MEM_LEVEL 35# endif 36# define OS_CODE 0x07 /* TODO: probably need to adjust that ... */ 37#endif 38 39#undef Assert 40 41@implementation NSData(gzip) 42 43- (NSData *)gzip { 44 return [self gzipWithLevel:Z_DEFAULT_COMPRESSION]; 45} 46 47static inline void putLong(uLong x, NSMutableData *data, IMP addBytes) { 48 int n; 49 for (n = 0; n < 4; n++) { 50 unsigned char c = (int)(x & 0xff); 51 addBytes(data, @selector(appendBytes:length:), &c, 1); 52 x >>= 8; 53 } 54} 55 56- (NSData *)gzipWithLevel:(int)_level { 57 NSMutableData *data = nil; 58 int errorCode = 0; 59 unsigned len = [self length]; 60 void *src = (void *)[self bytes]; 61 IMP addBytes = NULL; 62 char outBuf[4096]; 63 z_stream out; 64 uLong crc; 65 66 NSAssert1((_level >= NGGZipMinimalCompression && 67 _level <= NGGZipMaximalCompression) 68 || (_level == Z_DEFAULT_COMPRESSION), 69 @"invalid compression level %i (0-9)", _level); 70 71 data = [NSMutableData dataWithCapacity: 72 (len / 10 < 128) ? len : len / 10]; 73 addBytes = [data methodForSelector:@selector(appendBytes:length:)]; 74 75 out.zalloc = (alloc_func)NULL; 76 out.zfree = (free_func)NULL; 77 out.opaque = (voidpf)NULL; 78 out.next_out = (Byte*)&outBuf; 79 out.avail_out = sizeof(outBuf); 80 out.next_in = Z_NULL; 81 out.avail_in = 0; 82 errorCode = Z_OK; 83 crc = crc32(0L, Z_NULL, 0); 84 85 errorCode = deflateInit2(&out, _level, Z_DEFLATED, -MAX_WBITS, 86 DEF_MEM_LEVEL, 87 0); // windowBits is passed <0 to suppress zlib header 88 if (errorCode != Z_OK) { 89 NSLog(@"ERROR: could not init deflate !"); 90 return nil; 91 } 92 93 { // add gzip header 94 char buf[10] = { 95 0x1f, 0x8b, // magic 96 Z_DEFLATED, 0, // flags 97 0, 0, 0, 0, // time 98 0, OS_CODE // flags 99 }; 100 addBytes(data, @selector(appendBytes:length:), &buf, 10); 101 } 102 103 { // gz_write 104 out.next_in = src; 105 out.avail_in = len; 106 107 while (out.avail_in > 0) { 108 if (out.avail_out == 0) { 109 out.next_out = (void *)&outBuf; // reset buffer position 110 addBytes(data, @selector(appendBytes:length:), &outBuf, sizeof(outBuf)); 111 out.avail_out = sizeof(outBuf); 112 } 113 errorCode = deflate(&out, Z_NO_FLUSH); 114 if (errorCode != Z_OK) { 115 NSLog(@"ERROR: could not deflate chunk !"); 116 if (out.state) deflateEnd(&out); 117 return nil; 118 } 119 } 120 crc = crc32(crc, src, len); 121 } 122 123 { // gz_flush 124 BOOL done = NO; 125 126 out.next_in = NULL; 127 out.avail_in = 0; // should be zero already anyway 128 129 for (;;) { 130 len = sizeof(outBuf) - out.avail_out; 131 132 if (len > 0) { 133 addBytes(data, @selector(appendBytes:length:), &outBuf, len); 134 out.next_out = (void *)&outBuf; 135 out.avail_out = sizeof(outBuf); 136 } 137 if (done) 138 break; 139 errorCode = deflate(&out, Z_FINISH); 140 141 // deflate has finished flushing only when it hasn't used up 142 // all the available space in the output buffer: 143 done = (out.avail_out != 0 || errorCode == Z_STREAM_END); 144 145 if (errorCode != Z_OK && errorCode != Z_STREAM_END) 146 break; 147 } 148 if (errorCode != Z_STREAM_END) { 149 NSLog(@"ERROR: flush failed."); 150 if (out.state) deflateEnd(&out); 151 return nil; 152 } 153 } 154 { // write trailer (checksum and filesize) 155 putLong(crc, data, addBytes); 156 putLong(out.total_in, data, addBytes); 157 } 158 if (out.state) deflateEnd(&out); 159 160 return data; 161} 162 163- (NSData *)compress 164{ 165 return [self compressWithLevel: 3]; 166} 167 168- (NSData *)compressWithLevel: (int) level 169{ 170 NSData *result; 171 Bytef *destBuffer; 172 unsigned long destLen; 173 int rc; 174 175 destLen = [self length]; 176 destBuffer = NSZoneMalloc (NULL, destLen); 177 rc = compress2 (destBuffer, &destLen, [self bytes], destLen, level); 178 if (rc == Z_OK) 179 result = [NSData dataWithBytesNoCopy: destBuffer 180 length: destLen 181 freeWhenDone: YES]; 182 else 183 result = nil; 184 185 return result; 186} 187 188@end 189 190void __link_NSData_gzip(void) { 191 __link_NSData_gzip(); 192} 193