]> err.no Git - sope/blob - sope-core/NGExtensions/FdExt.subproj/NSData+gzip.m
renamed packages as discussed in the developer list
[sope] / sope-core / NGExtensions / FdExt.subproj / NSData+gzip.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 "NSData+gzip.h"
24 #include "common.h"
25
26 #ifdef Assert
27 #  undef Assert
28 #endif
29
30 #include <zlib.h>
31 #ifndef DEF_MEM_LEVEL /* zutil.h */
32 #  if MAX_MEM_LEVEL >= 8
33 #    define DEF_MEM_LEVEL 8
34 #  else
35 #    define DEF_MEM_LEVEL  MAX_MEM_LEVEL
36 #  endif
37 #  define OS_CODE  0x07 /* TODO: probably need to adjust that ... */
38 #endif
39
40 #undef Assert
41
42 @implementation NSData(gzip)
43
44 - (NSData *)gzip {
45   return [self gzipWithLevel:Z_DEFAULT_COMPRESSION];
46 }
47
48 static inline void putLong(uLong x, NSMutableData *data, IMP addBytes) {
49   int n;
50   for (n = 0; n < 4; n++) {
51     unsigned char c = (int)(x & 0xff);
52     addBytes(data, @selector(appendBytes:length:), &c, 1);
53     x >>= 8;
54   }
55 }
56
57 - (NSData *)gzipWithLevel:(int)_level {
58   NSMutableData *data     = nil;
59   int           errorCode = 0;
60   unsigned      len       = [self length];
61   void          *src      = (void *)[self bytes];
62   IMP           addBytes  = NULL;
63   char          outBuf[4096];
64   z_stream      out;
65   uLong         crc;
66
67   NSAssert1((_level >= NGGZipMinimalCompression &&
68              _level <= NGGZipMaximalCompression)
69             || (_level == Z_DEFAULT_COMPRESSION),
70             @"invalid compression level %i (0-9)", _level);
71
72   data = [NSMutableData dataWithCapacity:
73                           (len / 10 < 128) ? len : len / 10];
74   addBytes = [data methodForSelector:@selector(appendBytes:length:)];
75
76   out.zalloc    = (alloc_func)NULL;
77   out.zfree     = (free_func)NULL;
78   out.opaque    = (voidpf)NULL;
79   out.next_out  = (Byte*)&outBuf;
80   out.avail_out = sizeof(outBuf);
81   out.next_in   = Z_NULL;
82   out.avail_in  = 0;
83   errorCode     = Z_OK;
84   crc           = crc32(0L, Z_NULL, 0);
85
86   errorCode = deflateInit2(&out, _level, Z_DEFLATED, -MAX_WBITS,
87                            DEF_MEM_LEVEL,
88                            0); // windowBits is passed <0 to suppress zlib header
89   if (errorCode != Z_OK) {
90     NSLog(@"ERROR: could not init deflate !");
91     return nil;
92   }
93
94   { // add gzip header
95     char buf[10] = {
96       0x1f, 0x8b,    // magic
97       Z_DEFLATED, 0, // flags
98       0, 0, 0, 0,    // time
99       0, OS_CODE     // flags
100     };
101     addBytes(data, @selector(appendBytes:length:), &buf, 10);
102   }
103   
104   { // gz_write
105     out.next_in  = src;
106     out.avail_in = len;
107     
108     while (out.avail_in > 0) {
109       if (out.avail_out == 0) {
110         out.next_out = (void *)&outBuf; // reset buffer position
111         addBytes(data, @selector(appendBytes:length:), &outBuf, sizeof(outBuf));
112         out.avail_out = sizeof(outBuf);
113       }
114       errorCode = deflate(&out, Z_NO_FLUSH);
115       if (errorCode != Z_OK) {
116         NSLog(@"ERROR: could not deflate chunk !");
117         if (out.state) deflateEnd(&out);
118         return nil;
119       }
120     }
121     crc = crc32(crc, src, len);
122   }
123
124   { // gz_flush
125     BOOL done = NO;
126     
127     out.next_in  = NULL;
128     out.avail_in = 0; // should be zero already anyway
129     
130     for (;;) {
131       len = sizeof(outBuf) - out.avail_out;
132
133       if (len > 0) {
134         addBytes(data, @selector(appendBytes:length:), &outBuf, len);
135         out.next_out  = (void *)&outBuf;
136         out.avail_out = sizeof(outBuf);
137       }
138       if (done)
139         break;
140       errorCode = deflate(&out, Z_FINISH);
141
142       // deflate has finished flushing only when it hasn't used up
143       // all the available space in the output buffer: 
144       done = (out.avail_out != 0 || errorCode == Z_STREAM_END);
145
146       if (errorCode != Z_OK && errorCode != Z_STREAM_END)
147         break;
148     }
149     if (errorCode != Z_STREAM_END) {
150       NSLog(@"ERROR: flush failed.");
151       if (out.state) deflateEnd(&out);
152       return nil;
153     }
154   }
155   { // write trailer (checksum and filesize)
156     putLong(crc, data, addBytes);
157     putLong(out.total_in, data, addBytes);
158   }
159   if (out.state) deflateEnd(&out);
160
161   return data;
162 }
163
164 @end
165
166 void __link_NSData_gzip(void) {
167   __link_NSData_gzip();
168 }