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