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