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 <NGStreams/NGStreamExceptions.h> 23#include <NGStreams/NGGZipStream.h> 24#include <NGExtensions/NSData+gzip.h> 25#include "common.h" 26 27#ifdef Assert 28#undef Assert 29#endif 30 31#include <zlib.h> 32#ifndef DEF_MEM_LEVEL /* zutil.h */ 33# if MAX_MEM_LEVEL >= 8 34# define DEF_MEM_LEVEL 8 35# else 36# define DEF_MEM_LEVEL MAX_MEM_LEVEL 37# endif 38# define OS_CODE 0x07 /* TODO: probably need to adjust that ... */ 39#endif 40 41#undef Assert 42 43@implementation NGGZipStream 44 45- (id)initWithSource:(id<NGStream>)_source level:(int)_level { 46 if ((self = [super initWithSource:_source])) { 47 z_stream *zout; 48 49 NSAssert1((_level >= NGGZipMinimalCompression && 50 _level <= NGGZipMaximalCompression) 51 || (_level == Z_DEFAULT_COMPRESSION), 52 @"invalid compression level %i (0-9)", _level); 53 54 self->outBufLen = 2048; 55#if LIB_FOUNDATION_LIBRARY 56 self->outBuf = NSZoneMallocAtomic([self zone], self->outBufLen); 57 self->outp = NSZoneMallocAtomic([self zone], sizeof(z_stream)); 58#else 59 self->outBuf = NSZoneMalloc([self zone], self->outBufLen); 60 self->outp = NSZoneMalloc([self zone], sizeof(z_stream)); 61#endif 62 zout = self->outp; 63 zout->zalloc = (alloc_func)NULL; 64 zout->zfree = (free_func)NULL; 65 zout->opaque = (voidpf)NULL; 66 zout->next_out = self->outBuf; 67 zout->avail_out = self->outBufLen; 68 zout->next_in = Z_NULL; 69 zout->avail_in = 0; 70 self->crc = crc32(0L, Z_NULL, 0); 71 72 if (deflateInit2(zout, _level, Z_DEFLATED, -MAX_WBITS, 73 DEF_MEM_LEVEL, 0) != Z_OK) { 74 NSLog(@"Could not init deflate .."); 75 self = [self autorelease]; 76 return nil; 77 } 78 } 79 return self; 80} 81 82- (void)gcFinalize { 83 [self close]; 84} 85 86- (void)dealloc { 87 if (self->outBuf) NSZoneFree([self zone], self->outBuf); 88 if (self->outp) NSZoneFree([self zone], self->outp); 89 [self gcFinalize]; 90 [super dealloc]; 91} 92 93/* headers */ 94 95- (void)writeGZipHeader { 96 // gzip header 97 char buf[10] = { 98 0x1f, 0x8b, // magic 99 Z_DEFLATED, 0, // flags 100 0, 0, 0, 0, // time 101 0, OS_CODE // flags 102 }; 103 104 [self safeWriteBytes:buf count:10]; 105} 106 107static inline void putLong(NGGZipStream *self, uLong x) { 108 int n; 109 for (n = 0; n < 4; n++) { 110 unsigned char c = (int)(x & 0xff); 111 [self safeWriteBytes:&c count:1]; 112 x >>= 8; 113 } 114} 115- (void)writeGZipTrailer { 116 putLong(self, self->crc); 117 putLong(self, ((z_stream *)self->outp)->total_in); 118} 119 120/* primitives */ 121 122- (unsigned)readBytes:(void *)_buf count:(unsigned)_len { // decoder 123 [self notImplemented:_cmd]; 124 return -1; 125} 126 127- (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len { // encoder 128 z_stream *zout = self->outp; 129 130 if (!self->headerIsWritten) [self writeGZipHeader]; 131 132 { // gz_write 133 zout->next_in = (void*)_buf; 134 zout->avail_in = _len; 135 136 while (zout->avail_in > 0) { 137 int errorCode; 138 139 if (zout->avail_out == 0) { 140 [self safeWriteBytes:self->outBuf count:self->outBufLen]; 141 zout->next_out = self->outBuf; // reset buffer position 142 zout->avail_out = self->outBufLen; 143 } 144 errorCode = deflate(self->outp, Z_NO_FLUSH); 145 if (errorCode != Z_OK) { 146 if (zout->state) deflateEnd(self->outp); 147 [NGStreamException raiseWithStream:self 148 format:@"could not deflate chunk !"]; 149 } 150 } 151 self->crc = crc32(self->crc, _buf, _len); 152 } 153 return _len; 154} 155- (BOOL)safeWriteBytes:(const void *)_buf count:(unsigned)_len { // encoder 156 // gzip writes are safe 157 if ([self writeBytes:_buf count:_len] == NGStreamError) 158 return NO; 159 else 160 return YES; 161} 162 163- (void)close { 164 [self flush]; 165 [self writeGZipTrailer]; 166 if (((z_stream *)self->outp)->state) deflateEnd(self->outp); 167 [super close]; 168} 169 170- (void)flush { 171 int errorCode = Z_OK; 172 z_stream *zout = self->outp; 173 BOOL done = NO; 174 175 zout->next_in = NULL; 176 zout->avail_in = 0; // should be zero already anyway 177 178 while (1) { 179 int len = self->outBufLen - zout->avail_out; 180 181 if (len > 0) { 182 [self safeWriteBytes:self->outBuf count:len]; 183 zout->next_out = self->outBuf; 184 zout->avail_out = self->outBufLen; 185 } 186 if (done) 187 break; 188 errorCode = deflate(zout, Z_FINISH); 189 190 // deflate has finished flushing only when it hasn't used up 191 // all the available space in the output buffer: 192 done = (zout->avail_out != 0 || errorCode == Z_STREAM_END); 193 194 if (errorCode != Z_OK && errorCode != Z_STREAM_END) 195 break; 196 } 197 if (errorCode != Z_STREAM_END) { 198 if (zout->state) deflateEnd(zout); 199 [NGStreamException raiseWithStream:self format:@"flush failed"]; 200 } 201 202 [super flush]; 203} 204 205@end 206