source: Vago/zlib-1.2.8/contrib/iostream3/zfstream.cc@ 1076

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