]> git.tdb.fi Git - libs/core.git/blob - source/io/zlibcompressed.cpp
Add conversion from RFC3339 string to DateTime
[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::flush()
117 {
118 #ifdef WITH_ZLIB
119         if(mode==M_WRITE)
120         {
121                 while(1)
122                 {
123                         if(!compress_data(Z_SYNC_FLUSH))
124                                 break;
125
126                         // The flush is done when all input data has been consumed
127                         if(!priv->stream.avail_in)
128                                 break;
129                 }
130         }
131 #endif
132 }
133
134 unsigned ZlibCompressed::do_write(const char *data, unsigned size)
135 {
136         check_access(M_WRITE);
137
138         unsigned processed = 0;
139 #ifdef WITH_ZLIB
140         while(processed<size)
141         {
142                 unsigned free_in = (in_buffer+buffer_size-priv->stream.next_in);
143                 if(free_in<size && priv->stream.next_in>in_buffer)
144                 {
145                         // Not all of the data fits in the buffer, so make some more room
146                         copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
147                         priv->stream.next_in = in_buffer;
148                         free_in = buffer_size-priv->stream.avail_in;
149                 }
150
151                 if(free_in)
152                 {
153                         // Copy as much data into the input buffer as possible
154                         unsigned len = min(free_in, size-processed);
155                         copy(data+processed, data+processed+len, priv->stream.next_in+priv->stream.avail_in);
156                         priv->stream.avail_in += len;
157                         processed += len;
158                 }
159
160                 bool stalled = false;
161                 while(priv->stream.avail_in && !stalled)
162                         stalled = !compress_data(Z_NO_FLUSH);
163                 if(stalled)
164                         break;
165         }
166 #else
167         (void)data;
168         (void)size;
169 #endif
170
171         return processed;
172 }
173
174 bool ZlibCompressed::compress_data(int flush_mode)
175 {
176 #ifdef WITH_ZLIB
177         bool can_deflate = ((priv->stream.avail_in || flush_mode) && priv->stream.avail_out);
178         bool finished = false;
179         if(can_deflate)
180         {
181                 int ret = deflate(&priv->stream, flush_mode);
182                 if(flush_mode==Z_FINISH && ret==Z_STREAM_END)
183                         finished = true;
184                 else if(ret!=Z_OK)
185                         throw zlib_error("deflate", ret);
186         }
187
188         // Write compressed data into the underlying object
189         unsigned len = 0;
190         if(priv->stream.next_out>out_buffer)
191                 len = below.write(reinterpret_cast<char *>(out_buffer), priv->stream.next_out-out_buffer);
192         if(len>0)
193         {
194                 if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
195                         copy(out_buffer+len, priv->stream.next_out, out_buffer);
196                 priv->stream.avail_out += len;
197                 priv->stream.next_out -= len;
198         }
199         else if(!can_deflate)
200                 // We weren't able to do anything
201                 return false;
202
203         return !finished;
204 #else
205         (void)flush_mode;
206         return false;
207 #endif
208 }
209
210 unsigned ZlibCompressed::do_read(char *data, unsigned size)
211 {
212         check_access(M_READ);
213
214         unsigned processed = 0;
215 #ifdef WITH_ZLIB
216         while(processed<size)
217         {
218                 if(priv->stream.next_out>out_buffer)
219                 {
220                         // We have some pending output, give it out first
221                         unsigned len = min<unsigned>(priv->stream.next_out-out_buffer, size-processed);
222
223                         copy(out_buffer, out_buffer+len, data+processed);
224                         processed += len;
225
226                         if(len<static_cast<unsigned>(priv->stream.next_out-out_buffer))
227                                 copy(out_buffer+len, priv->stream.next_out, out_buffer);
228                         priv->stream.next_out -= len;
229                         priv->stream.avail_out += len;
230
231                         continue;
232                 }
233
234                 bool need_more_input = !priv->stream.avail_in;
235                 if(priv->stream.avail_in)
236                 {
237                         int ret = inflate(&priv->stream, Z_NO_FLUSH);
238                         if(ret==Z_STREAM_END)
239                                 stream_end = true;
240                         else if(ret!=Z_OK)
241                                 throw zlib_error("inflate", ret);
242                         need_more_input = (priv->stream.next_out==out_buffer);
243                 }
244
245                 if(need_more_input)
246                 {
247                         if(stream_end)
248                                 break;
249
250                         if(priv->stream.next_in>in_buffer)
251                                 copy(priv->stream.next_in, priv->stream.next_in+priv->stream.avail_in, in_buffer);
252                         priv->stream.next_in = in_buffer;
253
254                         unsigned len = below.read(reinterpret_cast<char *>(priv->stream.next_in), in_buffer+buffer_size-priv->stream.next_in);
255                         priv->stream.avail_in += len;
256                         if(!len && below.eof())
257                                 stream_end = true;
258                 }
259         }
260
261         if(size>0 && processed==0 && stream_end)
262                 set_eof();
263 #else
264         (void)data;
265         (void)size;
266 #endif
267
268         return processed;
269 }
270
271 } // namespace IO
272 } // namespace Msp