]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSData+gzip.m
fixed copyrights for 2005
[sope] / sope-core / NGExtensions / FdExt.subproj / NSData+gzip.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 "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
47 static 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 @end
164
165 void __link_NSData_gzip(void) {
166   __link_NSData_gzip();
167 }