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