]> err.no Git - sope/blob - sope-core/NGStreams/NGGZipStream.m
Drop apache 1 build-dependency
[sope] / sope-core / NGStreams / NGGZipStream.m
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 GNU_RUNTIME
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
107 static 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