Home | History | Annotate | Download | only in iostream3
      1 /*
      2  * A C++ I/O streams interface to the zlib gz* functions
      3  *
      4  * by Ludwig Schwardt <schwardt (at) sun.ac.za>
      5  * original version by Kevin Ruland <kevin (at) 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
    467