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 #ifndef ZFSTREAM_H
  11 #define ZFSTREAM_H
  12 
  13 #include <istream>  // not iostream, since we don't need cin/cout
  14 #include <ostream>
  15 #include "zlib.h"
  16 
  17 /*****************************************************************************/
  18 
  19 /**
  20  *  @brief  Gzipped file stream buffer class.
  21  *
  22  *  This class implements basic_filebuf for gzipped files. It doesn't yet support
  23  *  seeking (allowed by zlib but slow/limited), putback and read/write access
  24  *  (tricky). Otherwise, it attempts to be a drop-in replacement for the standard
  25  *  file streambuf.
  26 */
  27 class gzfilebuf : public std::streambuf
  28 {
  29 public:
  30   //  Default constructor.
  31   gzfilebuf();
  32 
  33   //  Destructor.
  34   virtual
  35   ~gzfilebuf();
  36 
  37   /**
  38    *  @brief  Set compression level and strategy on the fly.
  39    *  @param  comp_level  Compression level (see zlib.h for allowed values)
  40    *  @param  comp_strategy  Compression strategy (see zlib.h for allowed values)
  41    *  @return  Z_OK on success, Z_STREAM_ERROR otherwise.
  42    *
  43    *  Unfortunately, these parameters cannot be modified separately, as the
  44    *  previous zfstream version assumed. Since the strategy is seldom changed,
  45    *  it can default and setcompression(level) then becomes like the old
  46    *  setcompressionlevel(level).
  47   */
  48   int
  49   setcompression(int comp_level,
  50                  int comp_strategy = Z_DEFAULT_STRATEGY);
  51 
  52   /**
  53    *  @brief  Check if file is open.
  54    *  @return  True if file is open.
  55   */
  56   bool
  57   is_open() const { return (file != NULL); }
  58 
  59   /**
  60    *  @brief  Open gzipped file.
  61    *  @param  name  File name.
  62    *  @param  mode  Open mode flags.
  63    *  @return  @c this on success, NULL on failure.
  64   */
  65   gzfilebuf*
  66   open(const char* name,
  67        std::ios_base::openmode mode);
  68 
  69   /**
  70    *  @brief  Attach to already open gzipped file.
  71    *  @param  fd  File descriptor.
  72    *  @param  mode  Open mode flags.
  73    *  @return  @c this on success, NULL on failure.
  74   */
  75   gzfilebuf*
  76   attach(int fd,
  77          std::ios_base::openmode mode);
  78 
  79   /**
  80    *  @brief  Close gzipped file.
  81    *  @return  @c this on success, NULL on failure.
  82   */
  83   gzfilebuf*
  84   close();
  85 
  86 protected:
  87   /**
  88    *  @brief  Convert ios open mode int to mode string used by zlib.
  89    *  @return  True if valid mode flag combination.
  90   */
  91   bool
  92   open_mode(std::ios_base::openmode mode,
  93             char* c_mode) const;
  94 
  95   /**
  96    *  @brief  Number of characters available in stream buffer.
  97    *  @return  Number of characters.
  98    *
  99    *  This indicates number of characters in get area of stream buffer.
 100    *  These characters can be read without accessing the gzipped file.
 101   */
 102   virtual std::streamsize
 103   showmanyc();
 104 
 105   /**
 106    *  @brief  Fill get area from gzipped file.
 107    *  @return  First character in get area on success, EOF on error.
 108    *
 109    *  This actually reads characters from gzipped file to stream
 110    *  buffer. Always buffered.
 111   */
 112   virtual int_type
 113   underflow();
 114 
 115   /**
 116    *  @brief  Write put area to gzipped file.
 117    *  @param  c  Extra character to add to buffer contents.
 118    *  @return  Non-EOF on success, EOF on error.
 119    *
 120    *  This actually writes characters in stream buffer to
 121    *  gzipped file. With unbuffered output this is done one
 122    *  character at a time.
 123   */
 124   virtual int_type
 125   overflow(int_type c = traits_type::eof());
 126 
 127   /**
 128    *  @brief  Installs external stream buffer.
 129    *  @param  p  Pointer to char buffer.
 130    *  @param  n  Size of external buffer.
 131    *  @return  @c this on success, NULL on failure.
 132    *
 133    *  Call setbuf(0,0) to enable unbuffered output.
 134   */
 135   virtual std::streambuf*
 136   setbuf(char_type* p,
 137          std::streamsize n);
 138 
 139   /**
 140    *  @brief  Flush stream buffer to file.
 141    *  @return  0 on success, -1 on error.
 142    *
 143    *  This calls underflow(EOF) to do the job.
 144   */
 145   virtual int
 146   sync();
 147 
 148 //
 149 // Some future enhancements
 150 //
 151 //  virtual int_type uflow();
 152 //  virtual int_type pbackfail(int_type c = traits_type::eof());
 153 //  virtual pos_type
 154 //  seekoff(off_type off,
 155 //          std::ios_base::seekdir way,
 156 //          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);
 157 //  virtual pos_type
 158 //  seekpos(pos_type sp,
 159 //          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);
 160 
 161 private:
 162   /**
 163    *  @brief  Allocate internal buffer.
 164    *
 165    *  This function is safe to call multiple times. It will ensure
 166    *  that a proper internal buffer exists if it is required. If the
 167    *  buffer already exists or is external, the buffer pointers will be
 168    *  reset to their original state.
 169   */
 170   void
 171   enable_buffer();
 172 
 173   /**
 174    *  @brief  Destroy internal buffer.
 175    *
 176    *  This function is safe to call multiple times. It will ensure
 177    *  that the internal buffer is deallocated if it exists. In any
 178    *  case, it will also reset the buffer pointers.
 179   */
 180   void
 181   disable_buffer();
 182 
 183   /**
 184    *  Underlying file pointer.
 185   */
 186   gzFile file;
 187 
 188   /**
 189    *  Mode in which file was opened.
 190   */
 191   std::ios_base::openmode io_mode;
 192 
 193   /**
 194    *  @brief  True if this object owns file descriptor.
 195    *
 196    *  This makes the class responsible for closing the file
 197    *  upon destruction.
 198   */
 199   bool own_fd;
 200 
 201   /**
 202    *  @brief  Stream buffer.
 203    *
 204    *  For simplicity this remains allocated on the free store for the
 205    *  entire life span of the gzfilebuf object, unless replaced by setbuf.
 206   */
 207   char_type* buffer;
 208 
 209   /**
 210    *  @brief  Stream buffer size.
 211    *
 212    *  Defaults to system default buffer size (typically 8192 bytes).
 213    *  Modified by setbuf.
 214   */
 215   std::streamsize buffer_size;
 216 
 217   /**
 218    *  @brief  True if this object owns stream buffer.
 219    *
 220    *  This makes the class responsible for deleting the buffer
 221    *  upon destruction.
 222   */
 223   bool own_buffer;
 224 };
 225 
 226 /*****************************************************************************/
 227 
 228 /**
 229  *  @brief  Gzipped file input stream class.
 230  *
 231  *  This class implements ifstream for gzipped files. Seeking and putback
 232  *  is not supported yet.
 233 */
 234 class gzifstream : public std::istream
 235 {
 236 public:
 237   //  Default constructor
 238   gzifstream();
 239 
 240   /**
 241    *  @brief  Construct stream on gzipped file to be opened.
 242    *  @param  name  File name.
 243    *  @param  mode  Open mode flags (forced to contain ios::in).
 244   */
 245   explicit
 246   gzifstream(const char* name,
 247              std::ios_base::openmode mode = std::ios_base::in);
 248 
 249   /**
 250    *  @brief  Construct stream on already open gzipped file.
 251    *  @param  fd    File descriptor.
 252    *  @param  mode  Open mode flags (forced to contain ios::in).
 253   */
 254   explicit
 255   gzifstream(int fd,
 256              std::ios_base::openmode mode = std::ios_base::in);
 257 
 258   /**
 259    *  Obtain underlying stream buffer.
 260   */
 261   gzfilebuf*
 262   rdbuf() const
 263   { return const_cast<gzfilebuf*>(&sb); }
 264 
 265   /**
 266    *  @brief  Check if file is open.
 267    *  @return  True if file is open.
 268   */
 269   bool
 270   is_open() { return sb.is_open(); }
 271 
 272   /**
 273    *  @brief  Open gzipped file.
 274    *  @param  name  File name.
 275    *  @param  mode  Open mode flags (forced to contain ios::in).
 276    *
 277    *  Stream will be in state good() if file opens successfully;
 278    *  otherwise in state fail(). This differs from the behavior of
 279    *  ifstream, which never sets the state to good() and therefore
 280    *  won't allow you to reuse the stream for a second file unless
 281    *  you manually clear() the state. The choice is a matter of
 282    *  convenience.
 283   */
 284   void
 285   open(const char* name,
 286        std::ios_base::openmode mode = std::ios_base::in);
 287 
 288   /**
 289    *  @brief  Attach to already open gzipped file.
 290    *  @param  fd  File descriptor.
 291    *  @param  mode  Open mode flags (forced to contain ios::in).
 292    *
 293    *  Stream will be in state good() if attach succeeded; otherwise
 294    *  in state fail().
 295   */
 296   void
 297   attach(int fd,
 298          std::ios_base::openmode mode = std::ios_base::in);
 299 
 300   /**
 301    *  @brief  Close gzipped file.
 302    *
 303    *  Stream will be in state fail() if close failed.
 304   */
 305   void
 306   close();
 307 
 308 private:
 309   /**
 310    *  Underlying stream buffer.
 311   */
 312   gzfilebuf sb;
 313 };
 314 
 315 /*****************************************************************************/
 316 
 317 /**
 318  *  @brief  Gzipped file output stream class.
 319  *
 320  *  This class implements ofstream for gzipped files. Seeking and putback
 321  *  is not supported yet.
 322 */
 323 class gzofstream : public std::ostream
 324 {
 325 public:
 326   //  Default constructor
 327   gzofstream();
 328 
 329   /**
 330    *  @brief  Construct stream on gzipped file to be opened.
 331    *  @param  name  File name.
 332    *  @param  mode  Open mode flags (forced to contain ios::out).
 333   */
 334   explicit
 335   gzofstream(const char* name,
 336              std::ios_base::openmode mode = std::ios_base::out);
 337 
 338   /**
 339    *  @brief  Construct stream on already open gzipped file.
 340    *  @param  fd    File descriptor.
 341    *  @param  mode  Open mode flags (forced to contain ios::out).
 342   */
 343   explicit
 344   gzofstream(int fd,
 345              std::ios_base::openmode mode = std::ios_base::out);
 346 
 347   /**
 348    *  Obtain underlying stream buffer.
 349   */
 350   gzfilebuf*
 351   rdbuf() const
 352   { return const_cast<gzfilebuf*>(&sb); }
 353 
 354   /**
 355    *  @brief  Check if file is open.
 356    *  @return  True if file is open.
 357   */
 358   bool
 359   is_open() { return sb.is_open(); }
 360 
 361   /**
 362    *  @brief  Open gzipped file.
 363    *  @param  name  File name.
 364    *  @param  mode  Open mode flags (forced to contain ios::out).
 365    *
 366    *  Stream will be in state good() if file opens successfully;
 367    *  otherwise in state fail(). This differs from the behavior of
 368    *  ofstream, which never sets the state to good() and therefore
 369    *  won't allow you to reuse the stream for a second file unless
 370    *  you manually clear() the state. The choice is a matter of
 371    *  convenience.
 372   */
 373   void
 374   open(const char* name,
 375        std::ios_base::openmode mode = std::ios_base::out);
 376 
 377   /**
 378    *  @brief  Attach to already open gzipped file.
 379    *  @param  fd  File descriptor.
 380    *  @param  mode  Open mode flags (forced to contain ios::out).
 381    *
 382    *  Stream will be in state good() if attach succeeded; otherwise
 383    *  in state fail().
 384   */
 385   void
 386   attach(int fd,
 387          std::ios_base::openmode mode = std::ios_base::out);
 388 
 389   /**
 390    *  @brief  Close gzipped file.
 391    *
 392    *  Stream will be in state fail() if close failed.
 393   */
 394   void
 395   close();
 396 
 397 private:
 398   /**
 399    *  Underlying stream buffer.
 400   */
 401   gzfilebuf sb;
 402 };
 403 
 404 /*****************************************************************************/
 405 
 406 /**
 407  *  @brief  Gzipped file output stream manipulator class.
 408  *
 409  *  This class defines a two-argument manipulator for gzofstream. It is used
 410  *  as base for the setcompression(int,int) manipulator.
 411 */
 412 template<typename T1, typename T2>
 413   class gzomanip2
 414   {
 415   public:
 416     // Allows insertor to peek at internals
 417     template <typename Ta, typename Tb>
 418       friend gzofstream&
 419       operator<<(gzofstream&,
 420                  const gzomanip2<Ta,Tb>&);
 421 
 422     // Constructor
 423     gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2),
 424               T1 v1,
 425               T2 v2);
 426   private:
 427     // Underlying manipulator function
 428     gzofstream&
 429     (*func)(gzofstream&, T1, T2);
 430 
 431     // Arguments for manipulator function
 432     T1 val1;
 433     T2 val2;
 434   };
 435 
 436 /*****************************************************************************/
 437 
 438 // Manipulator function thunks through to stream buffer
 439 inline gzofstream&
 440 setcompression(gzofstream &gzs, int l, int s = Z_DEFAULT_STRATEGY)
 441 {
 442   (gzs.rdbuf())->setcompression(l, s);
 443   return gzs;
 444 }
 445 
 446 // Manipulator constructor stores arguments
 447 template<typename T1, typename T2>
 448   inline
 449   gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2),
 450                               T1 v1,
 451                               T2 v2)
 452   : func(f), val1(v1), val2(v2)
 453   { }
 454 
 455 // Insertor applies underlying manipulator function to stream
 456 template<typename T1, typename T2>
 457   inline gzofstream&
 458   operator<<(gzofstream& s, const gzomanip2<T1,T2>& m)
 459   { return (*m.func)(s, m.val1, m.val2); }
 460 
 461 // Insert this onto stream to simplify setting of compression level
 462 inline gzomanip2<int,int>
 463 setcompression(int l, int s = Z_DEFAULT_STRATEGY)
 464 { return gzomanip2<int,int>(&setcompression, l, s); }
 465 
 466 #endif // ZFSTREAM_H