99ae7f81f8845016e73e92d60f13083e7e672ee6
[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 {
43         mode = below.get_mode()&M_RDWR;
44         if(mode!=M_READ && mode!=M_WRITE)
45                 throw invalid_access(mode);
46
47         init(level);
48 }
49
50 ZlibCompressed::ZlibCompressed(Base &b, Mode m, unsigned level):
51         below(b)
52 {
53         mode = m&below.get_mode()&M_RDWR;
54         if(mode!=M_READ && mode!=M_WRITE)
55                 throw invalid_access(m);
56
57         init(level);
58 }
59
60 void ZlibCompressed::init(unsigned level)
61 {
62 #ifdef WITH_ZLIB
63         buffer_size = 1024;
64         in_buffer = 0;
65         out_buffer = 0;
66         stream_end = false;
67         priv = new Private;
68
69         if(mode==M_WRITE)
70         {
71                 int ret = deflateInit(&priv->stream, level);
72                 if(ret!=Z_OK)
73                         throw zlib_error("deflateInit", ret);
74         }
75         else
76         {
77                 int ret = inflateInit(&priv->stream);
78                 if(ret!=Z_OK)
79                         throw zlib_error("inflateInit", ret);
80         }
81
82         in_buffer = new unsigned char[buffer_size];
83         out_buffer = new unsigned char[buffer_size];
84
85         priv->stream.next_in = in_buffer;
86         priv->stream.avail_in = 0;
87         priv->stream.next_out = out_buffer;
88         priv->stream.avail_out = buffer_size;
89
90         below.signal_flush_required.connect(sigc::mem_fun(this, &ZlibCompressed::flush));
91 #else
92         (void)buffer_size;
93         (void)stream_end;
94         (void)level;
95         throw zlib_error("unsupported", -1);
96 #endif
97 }
98
99 ZlibCompressed::~ZlibCompressed()
100 {
101 #ifdef WITH_ZLIB
102         if(mode==M_WRITE)
103         {
104                 while(compress_data(Z_FINISH)) ;
105                 deflateEnd(&priv->stream);
106         }
107         else
108                 inflateEnd(&priv->stream);
109 #endif
110
111         delete[] in_buffer;
112         delete[] out_buffer;
113         delete priv;
114 }
115
116 void ZlibCompressed::set_block(bool)
117 {
118         throw logic_error("ZlibCompressed::set_block");
119 }
120
121 void ZlibCompressed::flush()
122 {
123 #ifdef WITH_ZLIB
124         if(mode==M_WRITE)
125         {
126                 while(1)
127                 {
128                         if(!compress_data(Z_SYNC_FLUSH))
129                                 break;
130
131                         // The flush is done when all input data has been consumed
132                         if(!priv->stream.avail_in)
133                                 break;
134                 }
135         }
136 #endif
137 }
138
139 unsigned ZlibCompressed::do_write(const char *data, unsigned size)
140 {
141         check_access(M_WRITE);
142
143         unsigned processed = 0;
144 #ifdef WITH_ZLIB
145         while(processed<size)
146         {
147                 unsigned free_in = (in_buffer+buffer_size-priv->stream.next_in);
148                 if(free_in<size && priv->stream.next_in>in_buffer)
149                 {
150                         // Not all of the data fits in the buffer, so make some more room
151                         copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
152                         priv->stream.next_in = in_buffer;
153                         free_in = buffer_size-priv->stream.avail_in;
154                 }
155
156                 if(free_in)
157                 {
158                         // Copy as much data into the input buffer as possible
159                         unsigned len = min(free_in, size-processed);
160                         copy(data+processed, data+processed+len, priv->stream.next_in+priv->stream.avail_in);
161                         priv->stream.avail_in += len;
162                         processed += len;
163                 }
164
165                 bool stalled = false;
166                 while(priv->stream.avail_in && !stalled)
167                         stalled = !compress_data(Z_NO_FLUSH);
168                 if(stalled)
169                         break;
170         }
171 #else
172         (void)data;
173         (void)size;
174 #endif
175
176         return processed;
177 }
178
179 bool ZlibCompressed::compress_data(int flush_mode)
180 {
181 #ifdef WITH_ZLIB
182         bool can_deflate = ((priv->stream.avail_in || flush_mode) && priv->stream.avail_out);
183         bool finished = false;
184         if(can_deflate)
185         {
186                 int ret = deflate(&priv->stream, flush_mode);
187                 if(flush_mode==Z_FINISH && ret==Z_STREAM_END)
188                         finished = true;
189                 else if(ret!=Z_OK)
190                         throw zlib_error("deflate", ret);
191         }
192
193         // Write compressed data into the underlying object
194         unsigned len = 0;
195         if(priv->stream.next_out>out_buffer)
196                 len = below.write(reinterpret_cast<char *>(out_buffer), priv->stream.next_out-out_buffer);
197         if(len>0)
198         {
199                 if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
200                         copy(out_buffer+len, priv->stream.next_out, out_buffer);
201                 priv->stream.avail_out += len;
202                 priv->stream.next_out -= len;
203         }
204         else if(!can_deflate)
205                 // We weren't able to do anything
206                 return false;
207
208         return !finished;
209 #else
210         (void)flush_mode;
211         return false;
212 #endif
213 }
214
215 unsigned ZlibCompressed::do_read(char *data, unsigned size)
216 {
217         check_access(M_READ);
218
219         unsigned processed = 0;
220 #ifdef WITH_ZLIB
221         while(processed<size)
222         {
223                 if(priv->stream.next_out>out_buffer)
224                 {
225                         // We have some pending output, give it out first
226                         unsigned len = min<unsigned>(priv->stream.next_out-out_buffer, size-processed);
227
228                         copy(out_buffer, out_buffer+len, data+processed);
229                         processed += len;
230
231                         if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
232                                 copy(out_buffer+len, priv->stream.next_out, out_buffer);
233                         priv->stream.next_out -= len;
234                         priv->stream.avail_out += len;
235
236                         continue;
237                 }
238
239                 bool need_more_input = !priv->stream.avail_in;
240                 if(priv->stream.avail_in)
241                 {
242                         int ret = inflate(&priv->stream, Z_NO_FLUSH);
243                         if(ret==Z_STREAM_END)
244                                 stream_end = true;
245                         else if(ret!=Z_OK)
246                                 throw zlib_error("inflate", ret);
247                         need_more_input = (priv->stream.next_out==out_buffer);
248                 }
249
250                 if(need_more_input)
251                 {
252                         if(stream_end)
253                                 break;
254
255                         if(priv->stream.next_in>in_buffer)
256                                 copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
257                         priv->stream.next_in = in_buffer;
258
259                         unsigned len = below.read(reinterpret_cast<char *>(priv->stream.next_in), in_buffer+buffer_size-priv->stream.next_in);
260                         priv->stream.avail_in += len;
261                         if(!len && below.eof())
262                                 stream_end = true;
263                 }
264         }
265
266         if(size>0 && processed==0 && stream_end)
267                 set_eof();
268 #else
269         (void)data;
270         (void)size;
271 #endif
272
273         return processed;
274 }
275
276 const Handle &ZlibCompressed::get_handle(Mode)
277 {
278         throw logic_error("ZlibCompressed::get_handle");
279 }
280
281 } // namespace IO
282 } // namespace Msp