2 * A C++ I/O streams interface to the zlib gz* functions
4 * by Ludwig Schwardt <schwardt@sun.ac.za>
5 * original version by Kevin Ruland <kevin@rodin.wustl.edu>
7 * This version is standard-compliant and compatible with gcc 3.x.
11 #include <cstring> // for strcpy, strcat, strlen (mode strings)
12 #include <cstdio> // for BUFSIZ
14 // Internal buffer sizes (default and "unbuffered" versions)
15 #define BIGBUFSIZE BUFSIZ
16 #define SMALLBUFSIZE 1
18 /*****************************************************************************/
20 // Default constructor
21 gzfilebuf::gzfilebuf()
22 : file(NULL
), io_mode(std::ios_base::openmode(0)), own_fd(false),
23 buffer(NULL
), buffer_size(BIGBUFSIZE
), own_buffer(true)
25 // No buffers to start with
26 this->disable_buffer();
30 gzfilebuf::~gzfilebuf()
32 // Sync output buffer and close only if responsible for file
33 // (i.e. attached streams should be left open at this stage)
37 // Make sure internal buffer is deallocated
38 this->disable_buffer();
41 // Set compression level and strategy
43 gzfilebuf::setcompression(int comp_level
,
46 return gzsetparams(file
, comp_level
, comp_strategy
);
51 gzfilebuf::open(const char *name
,
52 std::ios_base::openmode mode
)
54 // Fail if file already open
57 // Don't support simultaneous read/write access (yet)
58 if ((mode
& std::ios_base::in
) && (mode
& std::ios_base::out
))
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
))
66 // Attempt to open file
67 if ((file
= gzopen(name
, char_mode
)) == NULL
)
70 // On success, allocate internal buffer and set flags
71 this->enable_buffer();
77 // Attach to gzipped file
79 gzfilebuf::attach(int fd
,
80 std::ios_base::openmode mode
)
82 // Fail if file already open
85 // Don't support simultaneous read/write access (yet)
86 if ((mode
& std::ios_base::in
) && (mode
& std::ios_base::out
))
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
))
94 // Attempt to attach to file
95 if ((file
= gzdopen(fd
, char_mode
)) == NULL
)
98 // On success, allocate internal buffer and set flags
99 this->enable_buffer();
105 // Close gzipped file
109 // Fail immediately if no file is open
110 if (!this->is_open())
113 gzfilebuf
* retval
= this;
114 // Attempt to sync and close gzipped file
115 if (this->sync() == -1)
117 if (gzclose(file
) < 0)
119 // File is now gone anyway (postcondition [27.8.1.3.8])
122 // Destroy internal buffer if it exists
123 this->disable_buffer();
127 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
129 // Convert int open mode to mode string
131 gzfilebuf::open_mode(std::ios_base::openmode mode
,
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
;
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
)
147 if (!testi
&& testo
&& !testt
&& testa
)
149 if (!testi
&& testo
&& testt
&& !testa
)
151 if (testi
&& !testo
&& !testt
&& !testa
)
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+");
159 // Mode string should be empty for invalid combination of flags
160 if (strlen(c_mode
) == 0)
167 // Determine number of characters in internal get buffer
169 gzfilebuf::showmanyc()
171 // Calls to underflow will fail if file not opened for reading
172 if (!this->is_open() || !(io_mode
& std::ios_base::in
))
174 // Make sure get area is in use
175 if (this->gptr() && (this->gptr() < this->egptr()))
176 return std::streamsize(this->egptr() - this->gptr());
181 // Fill get area from gzipped file
183 gzfilebuf::underflow()
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()));
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();
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
202 this->setg(buffer
, buffer
, buffer
);
203 return traits_type::eof();
205 // Make all bytes read from file available as get area
206 this->setg(buffer
, buffer
, buffer
+ bytes_read
);
208 // Return next character in get area
209 return traits_type::to_int_type(*(this->gptr()));
212 // Write put area to gzipped file
214 gzfilebuf::overflow(int_type c
)
216 // Determine whether put area is in use
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()))
225 *(this->pptr()) = traits_type::to_char_type(c
);
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)
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
);
243 // Write extra character to file if not EOF
244 else if (!traits_type::eq_int_type(c
, traits_type::eof()))
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();
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
);
266 gzfilebuf::setbuf(char_type
* p
,
269 // First make sure stuff is sync'ed, for safety
270 if (this->sync() == -1)
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)
278 // Replace existing buffer (if any) with small internal buffer
279 this->disable_buffer();
283 this->enable_buffer();
287 // Replace existing buffer (if any) with external buffer
288 this->disable_buffer();
292 this->enable_buffer();
297 // Write put area to gzipped file (i.e. ensures that put area is empty)
301 return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0;
304 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
306 // Allocate internal buffer
308 gzfilebuf::enable_buffer()
310 // If internal buffer required, allocate one
311 if (own_buffer
&& !buffer
)
313 // Check for buffered vs. "unbuffered"
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);
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
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);
345 // Destroy internal buffer
347 gzfilebuf::disable_buffer()
349 // If internal buffer exists, deallocate it
350 if (own_buffer
&& buffer
)
352 // Preserve unbuffered status by zeroing size
362 // Reset buffer pointers to initial state if external buffer exists
363 this->setg(buffer
, buffer
, buffer
);
365 this->setp(buffer
, buffer
+ buffer_size
- 1);
371 /*****************************************************************************/
373 // Default constructor initializes stream buffer
374 gzifstream::gzifstream()
375 : std::istream(NULL
), sb()
378 // Initialize stream buffer and open file
379 gzifstream::gzifstream(const char* name
,
380 std::ios_base::openmode mode
)
381 : std::istream(NULL
), sb()
384 this->open(name
, mode
);
387 // Initialize stream buffer and attach to file
388 gzifstream::gzifstream(int fd
,
389 std::ios_base::openmode mode
)
390 : std::istream(NULL
), sb()
393 this->attach(fd
, mode
);
396 // Open file and go into fail() state if unsuccessful
398 gzifstream::open(const char* name
,
399 std::ios_base::openmode mode
)
401 if (!sb
.open(name
, mode
| std::ios_base::in
))
402 this->setstate(std::ios_base::failbit
);
407 // Attach to file and go into fail() state if unsuccessful
409 gzifstream::attach(int fd
,
410 std::ios_base::openmode mode
)
412 if (!sb
.attach(fd
, mode
| std::ios_base::in
))
413 this->setstate(std::ios_base::failbit
);
423 this->setstate(std::ios_base::failbit
);
426 /*****************************************************************************/
428 // Default constructor initializes stream buffer
429 gzofstream::gzofstream()
430 : std::ostream(NULL
), sb()
433 // Initialize stream buffer and open file
434 gzofstream::gzofstream(const char* name
,
435 std::ios_base::openmode mode
)
436 : std::ostream(NULL
), sb()
439 this->open(name
, mode
);
442 // Initialize stream buffer and attach to file
443 gzofstream::gzofstream(int fd
,
444 std::ios_base::openmode mode
)
445 : std::ostream(NULL
), sb()
448 this->attach(fd
, mode
);
451 // Open file and go into fail() state if unsuccessful
453 gzofstream::open(const char* name
,
454 std::ios_base::openmode mode
)
456 if (!sb
.open(name
, mode
| std::ios_base::out
))
457 this->setstate(std::ios_base::failbit
);
462 // Attach to file and go into fail() state if unsuccessful
464 gzofstream::attach(int fd
,
465 std::ios_base::openmode mode
)
467 if (!sb
.attach(fd
, mode
| std::ios_base::out
))
468 this->setstate(std::ios_base::failbit
);
478 this->setstate(std::ios_base::failbit
);