zfstream.cc revision 1.1.1.1 1 1.1 christos /*
2 1.1 christos * A C++ I/O streams interface to the zlib gz* functions
3 1.1 christos *
4 1.1 christos * by Ludwig Schwardt <schwardt (at) sun.ac.za>
5 1.1 christos * original version by Kevin Ruland <kevin (at) rodin.wustl.edu>
6 1.1 christos *
7 1.1 christos * This version is standard-compliant and compatible with gcc 3.x.
8 1.1 christos */
9 1.1 christos
10 1.1 christos #include "zfstream.h"
11 1.1 christos #include <cstring> // for strcpy, strcat, strlen (mode strings)
12 1.1 christos #include <cstdio> // for BUFSIZ
13 1.1 christos
14 1.1 christos // Internal buffer sizes (default and "unbuffered" versions)
15 1.1 christos #define BIGBUFSIZE BUFSIZ
16 1.1 christos #define SMALLBUFSIZE 1
17 1.1 christos
18 1.1 christos /*****************************************************************************/
19 1.1 christos
20 1.1 christos // Default constructor
21 1.1 christos gzfilebuf::gzfilebuf()
22 1.1 christos : file(NULL), io_mode(std::ios_base::openmode(0)), own_fd(false),
23 1.1 christos buffer(NULL), buffer_size(BIGBUFSIZE), own_buffer(true)
24 1.1 christos {
25 1.1 christos // No buffers to start with
26 1.1 christos this->disable_buffer();
27 1.1 christos }
28 1.1 christos
29 1.1 christos // Destructor
30 1.1 christos gzfilebuf::~gzfilebuf()
31 1.1 christos {
32 1.1 christos // Sync output buffer and close only if responsible for file
33 1.1 christos // (i.e. attached streams should be left open at this stage)
34 1.1 christos this->sync();
35 1.1 christos if (own_fd)
36 1.1 christos this->close();
37 1.1 christos // Make sure internal buffer is deallocated
38 1.1 christos this->disable_buffer();
39 1.1 christos }
40 1.1 christos
41 1.1 christos // Set compression level and strategy
42 1.1 christos int
43 1.1 christos gzfilebuf::setcompression(int comp_level,
44 1.1 christos int comp_strategy)
45 1.1 christos {
46 1.1 christos return gzsetparams(file, comp_level, comp_strategy);
47 1.1 christos }
48 1.1 christos
49 1.1 christos // Open gzipped file
50 1.1 christos gzfilebuf*
51 1.1 christos gzfilebuf::open(const char *name,
52 1.1 christos std::ios_base::openmode mode)
53 1.1 christos {
54 1.1 christos // Fail if file already open
55 1.1 christos if (this->is_open())
56 1.1 christos return NULL;
57 1.1 christos // Don't support simultaneous read/write access (yet)
58 1.1 christos if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
59 1.1 christos return NULL;
60 1.1 christos
61 1.1 christos // Build mode string for gzopen and check it [27.8.1.3.2]
62 1.1 christos char char_mode[6] = "\0\0\0\0\0";
63 1.1 christos if (!this->open_mode(mode, char_mode))
64 1.1 christos return NULL;
65 1.1 christos
66 1.1 christos // Attempt to open file
67 1.1 christos if ((file = gzopen(name, char_mode)) == NULL)
68 1.1 christos return NULL;
69 1.1 christos
70 1.1 christos // On success, allocate internal buffer and set flags
71 1.1 christos this->enable_buffer();
72 1.1 christos io_mode = mode;
73 1.1 christos own_fd = true;
74 1.1 christos return this;
75 1.1 christos }
76 1.1 christos
77 1.1 christos // Attach to gzipped file
78 1.1 christos gzfilebuf*
79 1.1 christos gzfilebuf::attach(int fd,
80 1.1 christos std::ios_base::openmode mode)
81 1.1 christos {
82 1.1 christos // Fail if file already open
83 1.1 christos if (this->is_open())
84 1.1 christos return NULL;
85 1.1 christos // Don't support simultaneous read/write access (yet)
86 1.1 christos if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
87 1.1 christos return NULL;
88 1.1 christos
89 1.1 christos // Build mode string for gzdopen and check it [27.8.1.3.2]
90 1.1 christos char char_mode[6] = "\0\0\0\0\0";
91 1.1 christos if (!this->open_mode(mode, char_mode))
92 1.1 christos return NULL;
93 1.1 christos
94 1.1 christos // Attempt to attach to file
95 1.1 christos if ((file = gzdopen(fd, char_mode)) == NULL)
96 1.1 christos return NULL;
97 1.1 christos
98 1.1 christos // On success, allocate internal buffer and set flags
99 1.1 christos this->enable_buffer();
100 1.1 christos io_mode = mode;
101 1.1 christos own_fd = false;
102 1.1 christos return this;
103 1.1 christos }
104 1.1 christos
105 1.1 christos // Close gzipped file
106 1.1 christos gzfilebuf*
107 1.1 christos gzfilebuf::close()
108 1.1 christos {
109 1.1 christos // Fail immediately if no file is open
110 1.1 christos if (!this->is_open())
111 1.1 christos return NULL;
112 1.1 christos // Assume success
113 1.1 christos gzfilebuf* retval = this;
114 1.1 christos // Attempt to sync and close gzipped file
115 1.1 christos if (this->sync() == -1)
116 1.1 christos retval = NULL;
117 1.1 christos if (gzclose(file) < 0)
118 1.1 christos retval = NULL;
119 1.1 christos // File is now gone anyway (postcondition [27.8.1.3.8])
120 1.1 christos file = NULL;
121 1.1 christos own_fd = false;
122 1.1 christos // Destroy internal buffer if it exists
123 1.1 christos this->disable_buffer();
124 1.1 christos return retval;
125 1.1 christos }
126 1.1 christos
127 1.1 christos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
128 1.1 christos
129 1.1 christos // Convert int open mode to mode string
130 1.1 christos bool
131 1.1 christos gzfilebuf::open_mode(std::ios_base::openmode mode,
132 1.1 christos char* c_mode) const
133 1.1 christos {
134 1.1 christos bool testb = mode & std::ios_base::binary;
135 1.1 christos bool testi = mode & std::ios_base::in;
136 1.1 christos bool testo = mode & std::ios_base::out;
137 1.1 christos bool testt = mode & std::ios_base::trunc;
138 1.1 christos bool testa = mode & std::ios_base::app;
139 1.1 christos
140 1.1 christos // Check for valid flag combinations - see [27.8.1.3.2] (Table 92)
141 1.1 christos // Original zfstream hardcoded the compression level to maximum here...
142 1.1 christos // Double the time for less than 1% size improvement seems
143 1.1 christos // excessive though - keeping it at the default level
144 1.1 christos // To change back, just append "9" to the next three mode strings
145 1.1 christos if (!testi && testo && !testt && !testa)
146 1.1 christos strcpy(c_mode, "w");
147 1.1 christos if (!testi && testo && !testt && testa)
148 1.1 christos strcpy(c_mode, "a");
149 1.1 christos if (!testi && testo && testt && !testa)
150 1.1 christos strcpy(c_mode, "w");
151 1.1 christos if (testi && !testo && !testt && !testa)
152 1.1 christos strcpy(c_mode, "r");
153 1.1 christos // No read/write mode yet
154 1.1 christos // if (testi && testo && !testt && !testa)
155 1.1 christos // strcpy(c_mode, "r+");
156 1.1 christos // if (testi && testo && testt && !testa)
157 1.1 christos // strcpy(c_mode, "w+");
158 1.1 christos
159 1.1 christos // Mode string should be empty for invalid combination of flags
160 1.1 christos if (strlen(c_mode) == 0)
161 1.1 christos return false;
162 1.1 christos if (testb)
163 1.1 christos strcat(c_mode, "b");
164 1.1 christos return true;
165 1.1 christos }
166 1.1 christos
167 1.1 christos // Determine number of characters in internal get buffer
168 1.1 christos std::streamsize
169 1.1 christos gzfilebuf::showmanyc()
170 1.1 christos {
171 1.1 christos // Calls to underflow will fail if file not opened for reading
172 1.1 christos if (!this->is_open() || !(io_mode & std::ios_base::in))
173 1.1 christos return -1;
174 1.1 christos // Make sure get area is in use
175 1.1 christos if (this->gptr() && (this->gptr() < this->egptr()))
176 1.1 christos return std::streamsize(this->egptr() - this->gptr());
177 1.1 christos else
178 1.1 christos return 0;
179 1.1 christos }
180 1.1 christos
181 1.1 christos // Fill get area from gzipped file
182 1.1 christos gzfilebuf::int_type
183 1.1 christos gzfilebuf::underflow()
184 1.1 christos {
185 1.1 christos // If something is left in the get area by chance, return it
186 1.1 christos // (this shouldn't normally happen, as underflow is only supposed
187 1.1 christos // to be called when gptr >= egptr, but it serves as error check)
188 1.1 christos if (this->gptr() && (this->gptr() < this->egptr()))
189 1.1 christos return traits_type::to_int_type(*(this->gptr()));
190 1.1 christos
191 1.1 christos // If the file hasn't been opened for reading, produce error
192 1.1 christos if (!this->is_open() || !(io_mode & std::ios_base::in))
193 1.1 christos return traits_type::eof();
194 1.1 christos
195 1.1 christos // Attempt to fill internal buffer from gzipped file
196 1.1 christos // (buffer must be guaranteed to exist...)
197 1.1 christos int bytes_read = gzread(file, buffer, buffer_size);
198 1.1 christos // Indicates error or EOF
199 1.1 christos if (bytes_read <= 0)
200 1.1 christos {
201 1.1 christos // Reset get area
202 1.1 christos this->setg(buffer, buffer, buffer);
203 1.1 christos return traits_type::eof();
204 1.1 christos }
205 1.1 christos // Make all bytes read from file available as get area
206 1.1 christos this->setg(buffer, buffer, buffer + bytes_read);
207 1.1 christos
208 1.1 christos // Return next character in get area
209 1.1 christos return traits_type::to_int_type(*(this->gptr()));
210 1.1 christos }
211 1.1 christos
212 1.1 christos // Write put area to gzipped file
213 1.1 christos gzfilebuf::int_type
214 1.1 christos gzfilebuf::overflow(int_type c)
215 1.1 christos {
216 1.1 christos // Determine whether put area is in use
217 1.1 christos if (this->pbase())
218 1.1 christos {
219 1.1 christos // Double-check pointer range
220 1.1 christos if (this->pptr() > this->epptr() || this->pptr() < this->pbase())
221 1.1 christos return traits_type::eof();
222 1.1 christos // Add extra character to buffer if not EOF
223 1.1 christos if (!traits_type::eq_int_type(c, traits_type::eof()))
224 1.1 christos {
225 1.1 christos *(this->pptr()) = traits_type::to_char_type(c);
226 1.1 christos this->pbump(1);
227 1.1 christos }
228 1.1 christos // Number of characters to write to file
229 1.1 christos int bytes_to_write = this->pptr() - this->pbase();
230 1.1 christos // Overflow doesn't fail if nothing is to be written
231 1.1 christos if (bytes_to_write > 0)
232 1.1 christos {
233 1.1 christos // If the file hasn't been opened for writing, produce error
234 1.1 christos if (!this->is_open() || !(io_mode & std::ios_base::out))
235 1.1 christos return traits_type::eof();
236 1.1 christos // If gzipped file won't accept all bytes written to it, fail
237 1.1 christos if (gzwrite(file, this->pbase(), bytes_to_write) != bytes_to_write)
238 1.1 christos return traits_type::eof();
239 1.1 christos // Reset next pointer to point to pbase on success
240 1.1 christos this->pbump(-bytes_to_write);
241 1.1 christos }
242 1.1 christos }
243 1.1 christos // Write extra character to file if not EOF
244 1.1 christos else if (!traits_type::eq_int_type(c, traits_type::eof()))
245 1.1 christos {
246 1.1 christos // If the file hasn't been opened for writing, produce error
247 1.1 christos if (!this->is_open() || !(io_mode & std::ios_base::out))
248 1.1 christos return traits_type::eof();
249 1.1 christos // Impromptu char buffer (allows "unbuffered" output)
250 1.1 christos char_type last_char = traits_type::to_char_type(c);
251 1.1 christos // If gzipped file won't accept this character, fail
252 1.1 christos if (gzwrite(file, &last_char, 1) != 1)
253 1.1 christos return traits_type::eof();
254 1.1 christos }
255 1.1 christos
256 1.1 christos // If you got here, you have succeeded (even if c was EOF)
257 1.1 christos // The return value should therefore be non-EOF
258 1.1 christos if (traits_type::eq_int_type(c, traits_type::eof()))
259 1.1 christos return traits_type::not_eof(c);
260 1.1 christos else
261 1.1 christos return c;
262 1.1 christos }
263 1.1 christos
264 1.1 christos // Assign new buffer
265 1.1 christos std::streambuf*
266 1.1 christos gzfilebuf::setbuf(char_type* p,
267 1.1 christos std::streamsize n)
268 1.1 christos {
269 1.1 christos // First make sure stuff is sync'ed, for safety
270 1.1 christos if (this->sync() == -1)
271 1.1 christos return NULL;
272 1.1 christos // If buffering is turned off on purpose via setbuf(0,0), still allocate one...
273 1.1 christos // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
274 1.1 christos // least a buffer of size 1 (very inefficient though, therefore make it bigger?)
275 1.1 christos // This follows from [27.5.2.4.3]/12 (gptr needs to point at something, it seems)
276 1.1 christos if (!p || !n)
277 1.1 christos {
278 1.1 christos // Replace existing buffer (if any) with small internal buffer
279 1.1 christos this->disable_buffer();
280 1.1 christos buffer = NULL;
281 1.1 christos buffer_size = 0;
282 1.1 christos own_buffer = true;
283 1.1 christos this->enable_buffer();
284 1.1 christos }
285 1.1 christos else
286 1.1 christos {
287 1.1 christos // Replace existing buffer (if any) with external buffer
288 1.1 christos this->disable_buffer();
289 1.1 christos buffer = p;
290 1.1 christos buffer_size = n;
291 1.1 christos own_buffer = false;
292 1.1 christos this->enable_buffer();
293 1.1 christos }
294 1.1 christos return this;
295 1.1 christos }
296 1.1 christos
297 1.1 christos // Write put area to gzipped file (i.e. ensures that put area is empty)
298 1.1 christos int
299 1.1 christos gzfilebuf::sync()
300 1.1 christos {
301 1.1 christos return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0;
302 1.1 christos }
303 1.1 christos
304 1.1 christos /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
305 1.1 christos
306 1.1 christos // Allocate internal buffer
307 1.1 christos void
308 1.1 christos gzfilebuf::enable_buffer()
309 1.1 christos {
310 1.1 christos // If internal buffer required, allocate one
311 1.1 christos if (own_buffer && !buffer)
312 1.1 christos {
313 1.1 christos // Check for buffered vs. "unbuffered"
314 1.1 christos if (buffer_size > 0)
315 1.1 christos {
316 1.1 christos // Allocate internal buffer
317 1.1 christos buffer = new char_type[buffer_size];
318 1.1 christos // Get area starts empty and will be expanded by underflow as need arises
319 1.1 christos this->setg(buffer, buffer, buffer);
320 1.1 christos // Setup entire internal buffer as put area.
321 1.1 christos // The one-past-end pointer actually points to the last element of the buffer,
322 1.1 christos // so that overflow(c) can safely add the extra character c to the sequence.
323 1.1 christos // These pointers remain in place for the duration of the buffer
324 1.1 christos this->setp(buffer, buffer + buffer_size - 1);
325 1.1 christos }
326 1.1 christos else
327 1.1 christos {
328 1.1 christos // Even in "unbuffered" case, (small?) get buffer is still required
329 1.1 christos buffer_size = SMALLBUFSIZE;
330 1.1 christos buffer = new char_type[buffer_size];
331 1.1 christos this->setg(buffer, buffer, buffer);
332 1.1 christos // "Unbuffered" means no put buffer
333 1.1 christos this->setp(0, 0);
334 1.1 christos }
335 1.1 christos }
336 1.1 christos else
337 1.1 christos {
338 1.1 christos // If buffer already allocated, reset buffer pointers just to make sure no
339 1.1 christos // stale chars are lying around
340 1.1 christos this->setg(buffer, buffer, buffer);
341 1.1 christos this->setp(buffer, buffer + buffer_size - 1);
342 1.1 christos }
343 1.1 christos }
344 1.1 christos
345 1.1 christos // Destroy internal buffer
346 1.1 christos void
347 1.1 christos gzfilebuf::disable_buffer()
348 1.1 christos {
349 1.1 christos // If internal buffer exists, deallocate it
350 1.1 christos if (own_buffer && buffer)
351 1.1 christos {
352 1.1 christos // Preserve unbuffered status by zeroing size
353 1.1 christos if (!this->pbase())
354 1.1 christos buffer_size = 0;
355 1.1 christos delete[] buffer;
356 1.1 christos buffer = NULL;
357 1.1 christos this->setg(0, 0, 0);
358 1.1 christos this->setp(0, 0);
359 1.1 christos }
360 1.1 christos else
361 1.1 christos {
362 1.1 christos // Reset buffer pointers to initial state if external buffer exists
363 1.1 christos this->setg(buffer, buffer, buffer);
364 1.1 christos if (buffer)
365 1.1 christos this->setp(buffer, buffer + buffer_size - 1);
366 1.1 christos else
367 1.1 christos this->setp(0, 0);
368 1.1 christos }
369 1.1 christos }
370 1.1 christos
371 1.1 christos /*****************************************************************************/
372 1.1 christos
373 1.1 christos // Default constructor initializes stream buffer
374 1.1 christos gzifstream::gzifstream()
375 1.1 christos : std::istream(NULL), sb()
376 1.1 christos { this->init(&sb); }
377 1.1 christos
378 1.1 christos // Initialize stream buffer and open file
379 1.1 christos gzifstream::gzifstream(const char* name,
380 1.1 christos std::ios_base::openmode mode)
381 1.1 christos : std::istream(NULL), sb()
382 1.1 christos {
383 1.1 christos this->init(&sb);
384 1.1 christos this->open(name, mode);
385 1.1 christos }
386 1.1 christos
387 1.1 christos // Initialize stream buffer and attach to file
388 1.1 christos gzifstream::gzifstream(int fd,
389 1.1 christos std::ios_base::openmode mode)
390 1.1 christos : std::istream(NULL), sb()
391 1.1 christos {
392 1.1 christos this->init(&sb);
393 1.1 christos this->attach(fd, mode);
394 1.1 christos }
395 1.1 christos
396 1.1 christos // Open file and go into fail() state if unsuccessful
397 1.1 christos void
398 1.1 christos gzifstream::open(const char* name,
399 1.1 christos std::ios_base::openmode mode)
400 1.1 christos {
401 1.1 christos if (!sb.open(name, mode | std::ios_base::in))
402 1.1 christos this->setstate(std::ios_base::failbit);
403 1.1 christos else
404 1.1 christos this->clear();
405 1.1 christos }
406 1.1 christos
407 1.1 christos // Attach to file and go into fail() state if unsuccessful
408 1.1 christos void
409 1.1 christos gzifstream::attach(int fd,
410 1.1 christos std::ios_base::openmode mode)
411 1.1 christos {
412 1.1 christos if (!sb.attach(fd, mode | std::ios_base::in))
413 1.1 christos this->setstate(std::ios_base::failbit);
414 1.1 christos else
415 1.1 christos this->clear();
416 1.1 christos }
417 1.1 christos
418 1.1 christos // Close file
419 1.1 christos void
420 1.1 christos gzifstream::close()
421 1.1 christos {
422 1.1 christos if (!sb.close())
423 1.1 christos this->setstate(std::ios_base::failbit);
424 1.1 christos }
425 1.1 christos
426 1.1 christos /*****************************************************************************/
427 1.1 christos
428 1.1 christos // Default constructor initializes stream buffer
429 1.1 christos gzofstream::gzofstream()
430 1.1 christos : std::ostream(NULL), sb()
431 1.1 christos { this->init(&sb); }
432 1.1 christos
433 1.1 christos // Initialize stream buffer and open file
434 1.1 christos gzofstream::gzofstream(const char* name,
435 1.1 christos std::ios_base::openmode mode)
436 1.1 christos : std::ostream(NULL), sb()
437 1.1 christos {
438 1.1 christos this->init(&sb);
439 1.1 christos this->open(name, mode);
440 1.1 christos }
441 1.1 christos
442 1.1 christos // Initialize stream buffer and attach to file
443 1.1 christos gzofstream::gzofstream(int fd,
444 1.1 christos std::ios_base::openmode mode)
445 1.1 christos : std::ostream(NULL), sb()
446 1.1 christos {
447 1.1 christos this->init(&sb);
448 1.1 christos this->attach(fd, mode);
449 1.1 christos }
450 1.1 christos
451 1.1 christos // Open file and go into fail() state if unsuccessful
452 1.1 christos void
453 1.1 christos gzofstream::open(const char* name,
454 1.1 christos std::ios_base::openmode mode)
455 1.1 christos {
456 1.1 christos if (!sb.open(name, mode | std::ios_base::out))
457 1.1 christos this->setstate(std::ios_base::failbit);
458 1.1 christos else
459 1.1 christos this->clear();
460 1.1 christos }
461 1.1 christos
462 1.1 christos // Attach to file and go into fail() state if unsuccessful
463 1.1 christos void
464 1.1 christos gzofstream::attach(int fd,
465 1.1 christos std::ios_base::openmode mode)
466 1.1 christos {
467 1.1 christos if (!sb.attach(fd, mode | std::ios_base::out))
468 1.1 christos this->setstate(std::ios_base::failbit);
469 1.1 christos else
470 1.1 christos this->clear();
471 1.1 christos }
472 1.1 christos
473 1.1 christos // Close file
474 1.1 christos void
475 1.1 christos gzofstream::close()
476 1.1 christos {
477 1.1 christos if (!sb.close())
478 1.1 christos this->setstate(std::ios_base::failbit);
479 1.1 christos }
480