]> git.tdb.fi Git - libs/core.git/blob - source/io/zlibcompressed.cpp
Add support for de/compression with zlib
[libs/core.git] / source / io / zlibcompressed.cpp
1 #ifdef WITH_ZLIB
2 #include <zlib.h>
3 #endif
4 #include "zlibcompressed.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace IO {
10
11 zlib_error::zlib_error(const std::string &w, int c):
12 #ifdef WITH_ZLIB
13         runtime_error(w+": "+zError(c)),
14 #else
15         runtime_error(w),
16 #endif
17         code_(c)
18 { }
19
20
21 struct ZlibCompressed::Private
22 {
23 #ifdef WITH_ZLIB
24         z_stream stream;
25 #endif
26
27         Private();
28 };
29
30 ZlibCompressed::Private::Private()
31 {
32 #ifdef WITH_ZLIB
33         stream.zalloc = 0;
34         stream.zfree = 0;
35         stream.opaque = 0;
36 #endif
37 }
38
39
40 ZlibCompressed::ZlibCompressed(Base &b, unsigned level):
41         below(b),
42         buffer_size(1024),
43         in_buffer(0),
44         out_buffer(0),
45         priv(0)
46 {
47 #ifdef WITH_ZLIB
48         mode = below.get_mode()&M_RDWR;
49         if(mode!=M_READ && mode!=M_WRITE)
50                 throw invalid_access(mode);
51
52         priv = new Private;
53
54         if(mode==M_WRITE)
55         {
56                 int ret = deflateInit(&priv->stream, level);
57                 if(ret!=Z_OK)
58                         throw zlib_error("deflateInit", ret);
59         }
60         else
61         {
62                 int ret = inflateInit(&priv->stream);
63                 if(ret!=Z_OK)
64                         throw zlib_error("inflateInit", ret);
65         }
66
67         in_buffer = new unsigned char[buffer_size];
68         out_buffer = new unsigned char[buffer_size];
69
70         priv->stream.next_in = in_buffer;
71         priv->stream.avail_in = 0;
72         priv->stream.next_out = out_buffer;
73         priv->stream.avail_out = buffer_size;
74
75         below.signal_flush_required.connect(sigc::mem_fun(this, &ZlibCompressed::flush));
76 #else
77         (void)level;
78         throw zlib_error("unsupported", -1);
79 #endif
80 }
81
82 ZlibCompressed::~ZlibCompressed()
83 {
84 #ifdef WITH_ZLIB
85         if(mode==M_WRITE)
86         {
87                 while(compress_data(Z_FINISH)) ;
88                 deflateEnd(&priv->stream);
89         }
90         else
91                 inflateEnd(&priv->stream);
92 #endif
93
94         delete[] in_buffer;
95         delete[] out_buffer;
96         delete priv;
97 }
98
99 void ZlibCompressed::flush()
100 {
101 #ifdef WITH_ZLIB
102         if(mode==M_WRITE)
103         {
104                 while(1)
105                 {
106                         if(!compress_data(Z_SYNC_FLUSH))
107                                 break;
108
109                         // The flush is done when all input data has been consumed
110                         if(!priv->stream.avail_in)
111                                 break;
112                 }
113         }
114 #endif
115 }
116
117 unsigned ZlibCompressed::do_write(const char *data, unsigned size)
118 {
119         check_access(M_WRITE);
120
121         unsigned processed = 0;
122 #ifdef WITH_ZLIB
123         while(processed<size)
124         {
125                 unsigned free_in = (in_buffer+buffer_size-priv->stream.next_in);
126                 if(free_in<size && priv->stream.next_in>in_buffer)
127                 {
128                         // Not all of the data fits in the buffer, so make some more room
129                         copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
130                         priv->stream.next_in = in_buffer;
131                         free_in = buffer_size-priv->stream.avail_in;
132                 }
133
134                 if(free_in)
135                 {
136                         // Copy as much data into the input buffer as possible
137                         unsigned len = min(free_in, size-processed);
138                         copy(data+processed, data+processed+len, priv->stream.next_in+priv->stream.avail_in);
139                         priv->stream.avail_in += len;
140                         processed += len;
141                 }
142
143                 bool stalled = false;
144                 while(priv->stream.avail_in && !stalled)
145                         stalled = !compress_data(Z_NO_FLUSH);
146                 if(stalled)
147                         break;
148         }
149 #else
150         (void)data;
151         (void)size;
152 #endif
153
154         return processed;
155 }
156
157 bool ZlibCompressed::compress_data(int flush_mode)
158 {
159 #ifdef WITH_ZLIB
160         bool can_deflate = ((priv->stream.avail_in || flush_mode) && priv->stream.avail_out);
161         bool finished = false;
162         if(can_deflate)
163         {
164                 int ret = deflate(&priv->stream, flush_mode);
165                 if(flush_mode==Z_FINISH && ret==Z_STREAM_END)
166                         finished = true;
167                 else if(ret!=Z_OK)
168                         throw zlib_error("deflate", ret);
169         }
170
171         // Write compressed data into the underlying object
172         unsigned len = 0;
173         if(priv->stream.next_out>out_buffer)
174                 len = below.write(reinterpret_cast<char *>(out_buffer), priv->stream.next_out-out_buffer);
175         if(len>0)
176         {
177                 if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
178                         copy(out_buffer+len, priv->stream.next_out, out_buffer);
179                 priv->stream.avail_out += len;
180                 priv->stream.next_out -= len;
181         }
182         else if(!can_deflate)
183                 // We weren't able to do anything
184                 return false;
185
186         return !finished;
187 #else
188         (void)flush_mode;
189         return false;
190 #endif
191 }
192
193 unsigned ZlibCompressed::do_read(char *data, unsigned size)
194 {
195         check_access(M_READ);
196
197         unsigned processed = 0;
198 #ifdef WITH_ZLIB
199         while(processed<size)
200         {
201                 if(priv->stream.next_out>out_buffer)
202                 {
203                         // We have some pending output, give it out first
204                         unsigned len = min<unsigned>(priv->stream.next_out-out_buffer, size-processed);
205
206                         copy(out_buffer, out_buffer+len, data+processed);
207                         processed += len;
208
209                         if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
210                                 copy(out_buffer+len, priv->stream.next_out, out_buffer);
211                         priv->stream.next_out -= len;
212                         priv->stream.avail_out += len;
213
214                         continue;
215                 }
216
217                 bool need_more_input = !priv->stream.avail_in;
218                 if(priv->stream.avail_in)
219                 {
220                         int ret = inflate(&priv->stream, Z_NO_FLUSH);
221                         if(ret==Z_STREAM_END)
222                                 set_eof();
223                         else if(ret!=Z_OK)
224                                 throw zlib_error("inflate", ret);
225                         need_more_input = (priv->stream.next_out==out_buffer);
226                 }
227
228                 if(need_more_input)
229                 {
230                         if(eof_flag)
231                                 break;
232
233                         if(priv->stream.next_in>in_buffer)
234                                 copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
235                         priv->stream.next_in = in_buffer;
236
237                         unsigned len = below.read(reinterpret_cast<char *>(priv->stream.next_in), in_buffer+buffer_size-priv->stream.next_in);
238                         priv->stream.avail_in += len;
239                         if(!len && below.eof())
240                                 set_eof();
241                 }
242         }
243 #else
244         (void)data;
245         (void)size;
246 #endif
247
248         return processed;
249 }
250
251 } // namespace IO
252 } // namespace Msp