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