zfstream.h 12.0 KB
Newer Older
M
Mark Adler 已提交
1 2
/*
 * A C++ I/O streams interface to the zlib gz* functions
M
Mark Adler 已提交
3
 *
M
Mark Adler 已提交
4 5
 * by Ludwig Schwardt <schwardt@sun.ac.za>
 * original version by Kevin Ruland <kevin@rodin.wustl.edu>
M
Mark Adler 已提交
6
 *
M
Mark Adler 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 * This version is standard-compliant and compatible with gcc 3.x.
 */

#ifndef ZFSTREAM_H
#define ZFSTREAM_H

#include <istream>  // not iostream, since we don't need cin/cout
#include <ostream>
#include "zlib.h"

/*****************************************************************************/

/**
 *  @brief  Gzipped file stream buffer class.
 *
 *  This class implements basic_filebuf for gzipped files. It doesn't yet support
M
Mark Adler 已提交
23 24
 *  seeking (allowed by zlib but slow/limited), putback and read/write access
 *  (tricky). Otherwise, it attempts to be a drop-in replacement for the standard
M
Mark Adler 已提交
25 26
 *  file streambuf.
*/
M
Mark Adler 已提交
27
class gzfilebuf : public std::streambuf
M
Mark Adler 已提交
28
{
M
Mark Adler 已提交
29
public:
M
Mark Adler 已提交
30 31
  //  Default constructor.
  gzfilebuf();
M
Mark Adler 已提交
32

M
Mark Adler 已提交
33
  //  Destructor.
M
Mark Adler 已提交
34
  virtual
M
Mark Adler 已提交
35 36 37 38
  ~gzfilebuf();

  /**
   *  @brief  Set compression level and strategy on the fly.
M
Mark Adler 已提交
39 40
   *  @param  comp_level  Compression level (see zlib.h for allowed values)
   *  @param  comp_strategy  Compression strategy (see zlib.h for allowed values)
M
Mark Adler 已提交
41
   *  @return  Z_OK on success, Z_STREAM_ERROR otherwise.
M
Mark Adler 已提交
42
   *
M
Mark Adler 已提交
43 44 45 46 47
   *  Unfortunately, these parameters cannot be modified separately, as the
   *  previous zfstream version assumed. Since the strategy is seldom changed,
   *  it can default and setcompression(level) then becomes like the old
   *  setcompressionlevel(level).
  */
M
Mark Adler 已提交
48 49 50 51
  int
  setcompression(int comp_level,
                 int comp_strategy = Z_DEFAULT_STRATEGY);

M
Mark Adler 已提交
52 53 54 55
  /**
   *  @brief  Check if file is open.
   *  @return  True if file is open.
  */
M
Mark Adler 已提交
56
  bool
M
Mark Adler 已提交
57
  is_open() const { return (file != NULL); }
M
Mark Adler 已提交
58

M
Mark Adler 已提交
59 60 61 62 63 64
  /**
   *  @brief  Open gzipped file.
   *  @param  name  File name.
   *  @param  mode  Open mode flags.
   *  @return  @c this on success, NULL on failure.
  */
M
Mark Adler 已提交
65 66
  gzfilebuf*
  open(const char* name,
M
Mark Adler 已提交
67
       std::ios_base::openmode mode);
M
Mark Adler 已提交
68

M
Mark Adler 已提交
69 70 71 72 73 74
  /**
   *  @brief  Attach to already open gzipped file.
   *  @param  fd  File descriptor.
   *  @param  mode  Open mode flags.
   *  @return  @c this on success, NULL on failure.
  */
M
Mark Adler 已提交
75 76 77 78
  gzfilebuf*
  attach(int fd,
         std::ios_base::openmode mode);

M
Mark Adler 已提交
79 80 81 82
  /**
   *  @brief  Close gzipped file.
   *  @return  @c this on success, NULL on failure.
  */
M
Mark Adler 已提交
83
  gzfilebuf*
M
Mark Adler 已提交
84
  close();
M
Mark Adler 已提交
85

M
Mark Adler 已提交
86 87 88 89 90
protected:
  /**
   *  @brief  Convert ios open mode int to mode string used by zlib.
   *  @return  True if valid mode flag combination.
  */
M
Mark Adler 已提交
91 92 93 94
  bool
  open_mode(std::ios_base::openmode mode,
            char* c_mode) const;

M
Mark Adler 已提交
95 96 97
  /**
   *  @brief  Number of characters available in stream buffer.
   *  @return  Number of characters.
M
Mark Adler 已提交
98
   *
M
Mark Adler 已提交
99 100 101 102 103
   *  This indicates number of characters in get area of stream buffer.
   *  These characters can be read without accessing the gzipped file.
  */
  virtual std::streamsize
  showmanyc();
M
Mark Adler 已提交
104

M
Mark Adler 已提交
105 106 107
  /**
   *  @brief  Fill get area from gzipped file.
   *  @return  First character in get area on success, EOF on error.
M
Mark Adler 已提交
108
   *
M
Mark Adler 已提交
109 110 111 112 113
   *  This actually reads characters from gzipped file to stream
   *  buffer. Always buffered.
  */
  virtual int_type
  underflow();
M
Mark Adler 已提交
114

M
Mark Adler 已提交
115 116 117 118
  /**
   *  @brief  Write put area to gzipped file.
   *  @param  c  Extra character to add to buffer contents.
   *  @return  Non-EOF on success, EOF on error.
M
Mark Adler 已提交
119 120 121
   *
   *  This actually writes characters in stream buffer to
   *  gzipped file. With unbuffered output this is done one
M
Mark Adler 已提交
122
   *  character at a time.
M
Mark Adler 已提交
123 124
  */
  virtual int_type
M
Mark Adler 已提交
125
  overflow(int_type c = traits_type::eof());
M
Mark Adler 已提交
126

M
Mark Adler 已提交
127 128 129 130 131
  /**
   *  @brief  Installs external stream buffer.
   *  @param  p  Pointer to char buffer.
   *  @param  n  Size of external buffer.
   *  @return  @c this on success, NULL on failure.
M
Mark Adler 已提交
132
   *
M
Mark Adler 已提交
133 134
   *  Call setbuf(0,0) to enable unbuffered output.
  */
M
Mark Adler 已提交
135 136 137
  virtual std::streambuf*
  setbuf(char_type* p,
         std::streamsize n);
M
Mark Adler 已提交
138 139 140 141

  /**
   *  @brief  Flush stream buffer to file.
   *  @return  0 on success, -1 on error.
M
Mark Adler 已提交
142
   *
M
Mark Adler 已提交
143 144
   *  This calls underflow(EOF) to do the job.
  */
M
Mark Adler 已提交
145
  virtual int
M
Mark Adler 已提交
146
  sync();
M
Mark Adler 已提交
147

M
Mark Adler 已提交
148 149
//
// Some future enhancements
M
Mark Adler 已提交
150
//
M
Mark Adler 已提交
151 152
//  virtual int_type uflow();
//  virtual int_type pbackfail(int_type c = traits_type::eof());
M
Mark Adler 已提交
153 154 155 156 157 158 159 160
//  virtual pos_type
//  seekoff(off_type off,
//          std::ios_base::seekdir way,
//          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);
//  virtual pos_type
//  seekpos(pos_type sp,
//          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);

M
Mark Adler 已提交
161 162 163
private:
  /**
   *  @brief  Allocate internal buffer.
M
Mark Adler 已提交
164
   *
M
Mark Adler 已提交
165 166 167 168 169
   *  This function is safe to call multiple times. It will ensure
   *  that a proper internal buffer exists if it is required. If the
   *  buffer already exists or is external, the buffer pointers will be
   *  reset to their original state.
  */
M
Mark Adler 已提交
170
  void
M
Mark Adler 已提交
171
  enable_buffer();
M
Mark Adler 已提交
172

M
Mark Adler 已提交
173 174
  /**
   *  @brief  Destroy internal buffer.
M
Mark Adler 已提交
175
   *
M
Mark Adler 已提交
176 177 178 179
   *  This function is safe to call multiple times. It will ensure
   *  that the internal buffer is deallocated if it exists. In any
   *  case, it will also reset the buffer pointers.
  */
M
Mark Adler 已提交
180
  void
M
Mark Adler 已提交
181
  disable_buffer();
M
Mark Adler 已提交
182

M
Mark Adler 已提交
183 184 185 186
  /**
   *  Underlying file pointer.
  */
  gzFile file;
M
Mark Adler 已提交
187

M
Mark Adler 已提交
188 189 190 191
  /**
   *  Mode in which file was opened.
  */
  std::ios_base::openmode io_mode;
M
Mark Adler 已提交
192

M
Mark Adler 已提交
193 194 195
  /**
   *  @brief  True if this object owns file descriptor.
   *
M
Mark Adler 已提交
196
   *  This makes the class responsible for closing the file
M
Mark Adler 已提交
197 198 199
   *  upon destruction.
  */
  bool own_fd;
M
Mark Adler 已提交
200

M
Mark Adler 已提交
201 202
  /**
   *  @brief  Stream buffer.
M
Mark Adler 已提交
203 204
   *
   *  For simplicity this remains allocated on the free store for the
M
Mark Adler 已提交
205 206 207
   *  entire life span of the gzfilebuf object, unless replaced by setbuf.
  */
  char_type* buffer;
M
Mark Adler 已提交
208

M
Mark Adler 已提交
209 210
  /**
   *  @brief  Stream buffer size.
M
Mark Adler 已提交
211
   *
M
Mark Adler 已提交
212 213 214 215
   *  Defaults to system default buffer size (typically 8192 bytes).
   *  Modified by setbuf.
  */
  std::streamsize buffer_size;
M
Mark Adler 已提交
216

M
Mark Adler 已提交
217 218 219
  /**
   *  @brief  True if this object owns stream buffer.
   *
M
Mark Adler 已提交
220
   *  This makes the class responsible for deleting the buffer
M
Mark Adler 已提交
221 222 223 224 225 226 227 228 229 230 231 232 233
   *  upon destruction.
  */
  bool own_buffer;
};

/*****************************************************************************/

/**
 *  @brief  Gzipped file input stream class.
 *
 *  This class implements ifstream for gzipped files. Seeking and putback
 *  is not supported yet.
*/
M
Mark Adler 已提交
234
class gzifstream : public std::istream
M
Mark Adler 已提交
235 236 237 238
{
public:
  //  Default constructor
  gzifstream();
M
Mark Adler 已提交
239

M
Mark Adler 已提交
240 241 242 243 244 245
  /**
   *  @brief  Construct stream on gzipped file to be opened.
   *  @param  name  File name.
   *  @param  mode  Open mode flags (forced to contain ios::in).
  */
  explicit
M
Mark Adler 已提交
246 247 248
  gzifstream(const char* name,
             std::ios_base::openmode mode = std::ios_base::in);

M
Mark Adler 已提交
249 250 251 252 253
  /**
   *  @brief  Construct stream on already open gzipped file.
   *  @param  fd    File descriptor.
   *  @param  mode  Open mode flags (forced to contain ios::in).
  */
M
Mark Adler 已提交
254 255 256
  explicit
  gzifstream(int fd,
             std::ios_base::openmode mode = std::ios_base::in);
M
Mark Adler 已提交
257 258 259

  /**
   *  Obtain underlying stream buffer.
M
Mark Adler 已提交
260 261
  */
  gzfilebuf*
M
Mark Adler 已提交
262
  rdbuf() const
M
Mark Adler 已提交
263
  { return const_cast<gzfilebuf*>(&sb); }
M
Mark Adler 已提交
264 265 266 267 268

  /**
   *  @brief  Check if file is open.
   *  @return  True if file is open.
  */
M
Mark Adler 已提交
269
  bool
M
Mark Adler 已提交
270
  is_open() { return sb.is_open(); }
M
Mark Adler 已提交
271

M
Mark Adler 已提交
272 273 274 275
  /**
   *  @brief  Open gzipped file.
   *  @param  name  File name.
   *  @param  mode  Open mode flags (forced to contain ios::in).
M
Mark Adler 已提交
276
   *
M
Mark Adler 已提交
277 278 279 280 281 282 283
   *  Stream will be in state good() if file opens successfully;
   *  otherwise in state fail(). This differs from the behavior of
   *  ifstream, which never sets the state to good() and therefore
   *  won't allow you to reuse the stream for a second file unless
   *  you manually clear() the state. The choice is a matter of
   *  convenience.
  */
M
Mark Adler 已提交
284 285
  void
  open(const char* name,
M
Mark Adler 已提交
286 287 288 289 290 291
       std::ios_base::openmode mode = std::ios_base::in);

  /**
   *  @brief  Attach to already open gzipped file.
   *  @param  fd  File descriptor.
   *  @param  mode  Open mode flags (forced to contain ios::in).
M
Mark Adler 已提交
292
   *
M
Mark Adler 已提交
293 294 295
   *  Stream will be in state good() if attach succeeded; otherwise
   *  in state fail().
  */
M
Mark Adler 已提交
296 297 298
  void
  attach(int fd,
         std::ios_base::openmode mode = std::ios_base::in);
M
Mark Adler 已提交
299 300 301

  /**
   *  @brief  Close gzipped file.
M
Mark Adler 已提交
302
   *
M
Mark Adler 已提交
303 304
   *  Stream will be in state fail() if close failed.
  */
M
Mark Adler 已提交
305
  void
M
Mark Adler 已提交
306
  close();
M
Mark Adler 已提交
307

M
Mark Adler 已提交
308 309 310
private:
  /**
   *  Underlying stream buffer.
M
Mark Adler 已提交
311
  */
M
Mark Adler 已提交
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
  gzfilebuf sb;
};

/*****************************************************************************/

/**
 *  @brief  Gzipped file output stream class.
 *
 *  This class implements ofstream for gzipped files. Seeking and putback
 *  is not supported yet.
*/
class gzofstream : public std::ostream
{
public:
  //  Default constructor
  gzofstream();
M
Mark Adler 已提交
328

M
Mark Adler 已提交
329 330 331 332 333 334
  /**
   *  @brief  Construct stream on gzipped file to be opened.
   *  @param  name  File name.
   *  @param  mode  Open mode flags (forced to contain ios::out).
  */
  explicit
M
Mark Adler 已提交
335 336 337
  gzofstream(const char* name,
             std::ios_base::openmode mode = std::ios_base::out);

M
Mark Adler 已提交
338 339 340 341 342
  /**
   *  @brief  Construct stream on already open gzipped file.
   *  @param  fd    File descriptor.
   *  @param  mode  Open mode flags (forced to contain ios::out).
  */
M
Mark Adler 已提交
343 344 345
  explicit
  gzofstream(int fd,
             std::ios_base::openmode mode = std::ios_base::out);
M
Mark Adler 已提交
346 347 348

  /**
   *  Obtain underlying stream buffer.
M
Mark Adler 已提交
349 350
  */
  gzfilebuf*
M
Mark Adler 已提交
351
  rdbuf() const
M
Mark Adler 已提交
352
  { return const_cast<gzfilebuf*>(&sb); }
M
Mark Adler 已提交
353 354 355 356 357

  /**
   *  @brief  Check if file is open.
   *  @return  True if file is open.
  */
M
Mark Adler 已提交
358
  bool
M
Mark Adler 已提交
359
  is_open() { return sb.is_open(); }
M
Mark Adler 已提交
360

M
Mark Adler 已提交
361 362 363 364
  /**
   *  @brief  Open gzipped file.
   *  @param  name  File name.
   *  @param  mode  Open mode flags (forced to contain ios::out).
M
Mark Adler 已提交
365
   *
M
Mark Adler 已提交
366 367 368 369 370 371 372
   *  Stream will be in state good() if file opens successfully;
   *  otherwise in state fail(). This differs from the behavior of
   *  ofstream, which never sets the state to good() and therefore
   *  won't allow you to reuse the stream for a second file unless
   *  you manually clear() the state. The choice is a matter of
   *  convenience.
  */
M
Mark Adler 已提交
373 374
  void
  open(const char* name,
M
Mark Adler 已提交
375 376 377 378 379 380
       std::ios_base::openmode mode = std::ios_base::out);

  /**
   *  @brief  Attach to already open gzipped file.
   *  @param  fd  File descriptor.
   *  @param  mode  Open mode flags (forced to contain ios::out).
M
Mark Adler 已提交
381
   *
M
Mark Adler 已提交
382 383 384
   *  Stream will be in state good() if attach succeeded; otherwise
   *  in state fail().
  */
M
Mark Adler 已提交
385 386 387
  void
  attach(int fd,
         std::ios_base::openmode mode = std::ios_base::out);
M
Mark Adler 已提交
388 389 390

  /**
   *  @brief  Close gzipped file.
M
Mark Adler 已提交
391
   *
M
Mark Adler 已提交
392 393
   *  Stream will be in state fail() if close failed.
  */
M
Mark Adler 已提交
394
  void
M
Mark Adler 已提交
395
  close();
M
Mark Adler 已提交
396

M
Mark Adler 已提交
397 398 399
private:
  /**
   *  Underlying stream buffer.
M
Mark Adler 已提交
400
  */
M
Mark Adler 已提交
401 402 403 404
  gzfilebuf sb;
};

/*****************************************************************************/
M
Mark Adler 已提交
405

M
Mark Adler 已提交
406 407 408 409 410 411 412 413 414 415 416 417
/**
 *  @brief  Gzipped file output stream manipulator class.
 *
 *  This class defines a two-argument manipulator for gzofstream. It is used
 *  as base for the setcompression(int,int) manipulator.
*/
template<typename T1, typename T2>
  class gzomanip2
  {
  public:
    // Allows insertor to peek at internals
    template <typename Ta, typename Tb>
M
Mark Adler 已提交
418 419 420 421
      friend gzofstream&
      operator<<(gzofstream&,
                 const gzomanip2<Ta,Tb>&);

M
Mark Adler 已提交
422 423
    // Constructor
    gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2),
M
Mark Adler 已提交
424 425
              T1 v1,
              T2 v2);
M
Mark Adler 已提交
426 427 428 429
  private:
    // Underlying manipulator function
    gzofstream&
    (*func)(gzofstream&, T1, T2);
M
Mark Adler 已提交
430 431

    // Arguments for manipulator function
M
Mark Adler 已提交
432 433 434 435 436
    T1 val1;
    T2 val2;
  };

/*****************************************************************************/
M
Mark Adler 已提交
437

M
Mark Adler 已提交
438
// Manipulator function thunks through to stream buffer
M
Mark Adler 已提交
439
inline gzofstream&
M
Mark Adler 已提交
440 441 442 443 444 445 446 447
setcompression(gzofstream &gzs, int l, int s = Z_DEFAULT_STRATEGY)
{
  (gzs.rdbuf())->setcompression(l, s);
  return gzs;
}

// Manipulator constructor stores arguments
template<typename T1, typename T2>
M
Mark Adler 已提交
448
  inline
M
Mark Adler 已提交
449
  gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2),
M
Mark Adler 已提交
450 451
                              T1 v1,
                              T2 v2)
M
Mark Adler 已提交
452 453 454 455 456
  : func(f), val1(v1), val2(v2)
  { }

// Insertor applies underlying manipulator function to stream
template<typename T1, typename T2>
M
Mark Adler 已提交
457 458
  inline gzofstream&
  operator<<(gzofstream& s, const gzomanip2<T1,T2>& m)
M
Mark Adler 已提交
459 460 461
  { return (*m.func)(s, m.val1, m.val2); }

// Insert this onto stream to simplify setting of compression level
M
Mark Adler 已提交
462 463
inline gzomanip2<int,int>
setcompression(int l, int s = Z_DEFAULT_STRATEGY)
M
Mark Adler 已提交
464 465 466
{ return gzomanip2<int,int>(&setcompression, l, s); }

#endif // ZFSTREAM_H