467 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			467 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * A C++ I/O streams interface to the zlib gz* functions | ||
|  |  * | ||
|  |  * by Ludwig Schwardt <schwardt@sun.ac.za> | ||
|  |  * original version by Kevin Ruland <kevin@rodin.wustl.edu> | ||
|  |  * | ||
|  |  * 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 | ||
|  |  *  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 | ||
|  |  *  file streambuf. | ||
|  | */ | ||
|  | class gzfilebuf : public std::streambuf | ||
|  | { | ||
|  | public: | ||
|  |   //  Default constructor.
 | ||
|  |   gzfilebuf(); | ||
|  | 
 | ||
|  |   //  Destructor.
 | ||
|  |   virtual | ||
|  |   ~gzfilebuf(); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Set compression level and strategy on the fly. | ||
|  |    *  @param  comp_level  Compression level (see zlib.h for allowed values) | ||
|  |    *  @param  comp_strategy  Compression strategy (see zlib.h for allowed values) | ||
|  |    *  @return  Z_OK on success, Z_STREAM_ERROR otherwise. | ||
|  |    * | ||
|  |    *  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). | ||
|  |   */ | ||
|  |   int | ||
|  |   setcompression(int comp_level, | ||
|  |                  int comp_strategy = Z_DEFAULT_STRATEGY); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Check if file is open. | ||
|  |    *  @return  True if file is open. | ||
|  |   */ | ||
|  |   bool | ||
|  |   is_open() const { return (file != NULL); } | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Open gzipped file. | ||
|  |    *  @param  name  File name. | ||
|  |    *  @param  mode  Open mode flags. | ||
|  |    *  @return  @c this on success, NULL on failure. | ||
|  |   */ | ||
|  |   gzfilebuf* | ||
|  |   open(const char* name, | ||
|  |        std::ios_base::openmode mode); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Attach to already open gzipped file. | ||
|  |    *  @param  fd  File descriptor. | ||
|  |    *  @param  mode  Open mode flags. | ||
|  |    *  @return  @c this on success, NULL on failure. | ||
|  |   */ | ||
|  |   gzfilebuf* | ||
|  |   attach(int fd, | ||
|  |          std::ios_base::openmode mode); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Close gzipped file. | ||
|  |    *  @return  @c this on success, NULL on failure. | ||
|  |   */ | ||
|  |   gzfilebuf* | ||
|  |   close(); | ||
|  | 
 | ||
|  | protected: | ||
|  |   /**
 | ||
|  |    *  @brief  Convert ios open mode int to mode string used by zlib. | ||
|  |    *  @return  True if valid mode flag combination. | ||
|  |   */ | ||
|  |   bool | ||
|  |   open_mode(std::ios_base::openmode mode, | ||
|  |             char* c_mode) const; | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Number of characters available in stream buffer. | ||
|  |    *  @return  Number of characters. | ||
|  |    * | ||
|  |    *  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(); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Fill get area from gzipped file. | ||
|  |    *  @return  First character in get area on success, EOF on error. | ||
|  |    * | ||
|  |    *  This actually reads characters from gzipped file to stream | ||
|  |    *  buffer. Always buffered. | ||
|  |   */ | ||
|  |   virtual int_type | ||
|  |   underflow(); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Write put area to gzipped file. | ||
|  |    *  @param  c  Extra character to add to buffer contents. | ||
|  |    *  @return  Non-EOF on success, EOF on error. | ||
|  |    * | ||
|  |    *  This actually writes characters in stream buffer to | ||
|  |    *  gzipped file. With unbuffered output this is done one | ||
|  |    *  character at a time. | ||
|  |   */ | ||
|  |   virtual int_type | ||
|  |   overflow(int_type c = traits_type::eof()); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @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. | ||
|  |    * | ||
|  |    *  Call setbuf(0,0) to enable unbuffered output. | ||
|  |   */ | ||
|  |   virtual std::streambuf* | ||
|  |   setbuf(char_type* p, | ||
|  |          std::streamsize n); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Flush stream buffer to file. | ||
|  |    *  @return  0 on success, -1 on error. | ||
|  |    * | ||
|  |    *  This calls underflow(EOF) to do the job. | ||
|  |   */ | ||
|  |   virtual int | ||
|  |   sync(); | ||
|  | 
 | ||
|  | //
 | ||
|  | // Some future enhancements
 | ||
|  | //
 | ||
|  | //  virtual int_type uflow();
 | ||
|  | //  virtual int_type pbackfail(int_type c = traits_type::eof());
 | ||
|  | //  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);
 | ||
|  | 
 | ||
|  | private: | ||
|  |   /**
 | ||
|  |    *  @brief  Allocate internal buffer. | ||
|  |    * | ||
|  |    *  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. | ||
|  |   */ | ||
|  |   void | ||
|  |   enable_buffer(); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Destroy internal buffer. | ||
|  |    * | ||
|  |    *  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. | ||
|  |   */ | ||
|  |   void | ||
|  |   disable_buffer(); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  Underlying file pointer. | ||
|  |   */ | ||
|  |   gzFile file; | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  Mode in which file was opened. | ||
|  |   */ | ||
|  |   std::ios_base::openmode io_mode; | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  True if this object owns file descriptor. | ||
|  |    * | ||
|  |    *  This makes the class responsible for closing the file | ||
|  |    *  upon destruction. | ||
|  |   */ | ||
|  |   bool own_fd; | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Stream buffer. | ||
|  |    * | ||
|  |    *  For simplicity this remains allocated on the free store for the | ||
|  |    *  entire life span of the gzfilebuf object, unless replaced by setbuf. | ||
|  |   */ | ||
|  |   char_type* buffer; | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Stream buffer size. | ||
|  |    * | ||
|  |    *  Defaults to system default buffer size (typically 8192 bytes). | ||
|  |    *  Modified by setbuf. | ||
|  |   */ | ||
|  |   std::streamsize buffer_size; | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  True if this object owns stream buffer. | ||
|  |    * | ||
|  |    *  This makes the class responsible for deleting the buffer | ||
|  |    *  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. | ||
|  | */ | ||
|  | class gzifstream : public std::istream | ||
|  | { | ||
|  | public: | ||
|  |   //  Default constructor
 | ||
|  |   gzifstream(); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Construct stream on gzipped file to be opened. | ||
|  |    *  @param  name  File name. | ||
|  |    *  @param  mode  Open mode flags (forced to contain ios::in). | ||
|  |   */ | ||
|  |   explicit | ||
|  |   gzifstream(const char* name, | ||
|  |              std::ios_base::openmode mode = std::ios_base::in); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Construct stream on already open gzipped file. | ||
|  |    *  @param  fd    File descriptor. | ||
|  |    *  @param  mode  Open mode flags (forced to contain ios::in). | ||
|  |   */ | ||
|  |   explicit | ||
|  |   gzifstream(int fd, | ||
|  |              std::ios_base::openmode mode = std::ios_base::in); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  Obtain underlying stream buffer. | ||
|  |   */ | ||
|  |   gzfilebuf* | ||
|  |   rdbuf() const | ||
|  |   { return const_cast<gzfilebuf*>(&sb); } | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Check if file is open. | ||
|  |    *  @return  True if file is open. | ||
|  |   */ | ||
|  |   bool | ||
|  |   is_open() { return sb.is_open(); } | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Open gzipped file. | ||
|  |    *  @param  name  File name. | ||
|  |    *  @param  mode  Open mode flags (forced to contain ios::in). | ||
|  |    * | ||
|  |    *  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. | ||
|  |   */ | ||
|  |   void | ||
|  |   open(const char* name, | ||
|  |        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). | ||
|  |    * | ||
|  |    *  Stream will be in state good() if attach succeeded; otherwise | ||
|  |    *  in state fail(). | ||
|  |   */ | ||
|  |   void | ||
|  |   attach(int fd, | ||
|  |          std::ios_base::openmode mode = std::ios_base::in); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Close gzipped file. | ||
|  |    * | ||
|  |    *  Stream will be in state fail() if close failed. | ||
|  |   */ | ||
|  |   void | ||
|  |   close(); | ||
|  | 
 | ||
|  | private: | ||
|  |   /**
 | ||
|  |    *  Underlying stream buffer. | ||
|  |   */ | ||
|  |   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(); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Construct stream on gzipped file to be opened. | ||
|  |    *  @param  name  File name. | ||
|  |    *  @param  mode  Open mode flags (forced to contain ios::out). | ||
|  |   */ | ||
|  |   explicit | ||
|  |   gzofstream(const char* name, | ||
|  |              std::ios_base::openmode mode = std::ios_base::out); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Construct stream on already open gzipped file. | ||
|  |    *  @param  fd    File descriptor. | ||
|  |    *  @param  mode  Open mode flags (forced to contain ios::out). | ||
|  |   */ | ||
|  |   explicit | ||
|  |   gzofstream(int fd, | ||
|  |              std::ios_base::openmode mode = std::ios_base::out); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  Obtain underlying stream buffer. | ||
|  |   */ | ||
|  |   gzfilebuf* | ||
|  |   rdbuf() const | ||
|  |   { return const_cast<gzfilebuf*>(&sb); } | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Check if file is open. | ||
|  |    *  @return  True if file is open. | ||
|  |   */ | ||
|  |   bool | ||
|  |   is_open() { return sb.is_open(); } | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Open gzipped file. | ||
|  |    *  @param  name  File name. | ||
|  |    *  @param  mode  Open mode flags (forced to contain ios::out). | ||
|  |    * | ||
|  |    *  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. | ||
|  |   */ | ||
|  |   void | ||
|  |   open(const char* name, | ||
|  |        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). | ||
|  |    * | ||
|  |    *  Stream will be in state good() if attach succeeded; otherwise | ||
|  |    *  in state fail(). | ||
|  |   */ | ||
|  |   void | ||
|  |   attach(int fd, | ||
|  |          std::ios_base::openmode mode = std::ios_base::out); | ||
|  | 
 | ||
|  |   /**
 | ||
|  |    *  @brief  Close gzipped file. | ||
|  |    * | ||
|  |    *  Stream will be in state fail() if close failed. | ||
|  |   */ | ||
|  |   void | ||
|  |   close(); | ||
|  | 
 | ||
|  | private: | ||
|  |   /**
 | ||
|  |    *  Underlying stream buffer. | ||
|  |   */ | ||
|  |   gzfilebuf sb; | ||
|  | }; | ||
|  | 
 | ||
|  | /*****************************************************************************/ | ||
|  | 
 | ||
|  | /**
 | ||
|  |  *  @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> | ||
|  |       friend gzofstream& | ||
|  |       operator<<(gzofstream&, | ||
|  |                  const gzomanip2<Ta,Tb>&); | ||
|  | 
 | ||
|  |     // Constructor
 | ||
|  |     gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2), | ||
|  |               T1 v1, | ||
|  |               T2 v2); | ||
|  |   private: | ||
|  |     // Underlying manipulator function
 | ||
|  |     gzofstream& | ||
|  |     (*func)(gzofstream&, T1, T2); | ||
|  | 
 | ||
|  |     // Arguments for manipulator function
 | ||
|  |     T1 val1; | ||
|  |     T2 val2; | ||
|  |   }; | ||
|  | 
 | ||
|  | /*****************************************************************************/ | ||
|  | 
 | ||
|  | // Manipulator function thunks through to stream buffer
 | ||
|  | inline gzofstream& | ||
|  | 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> | ||
|  |   inline | ||
|  |   gzomanip2<T1,T2>::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2), | ||
|  |                               T1 v1, | ||
|  |                               T2 v2) | ||
|  |   : func(f), val1(v1), val2(v2) | ||
|  |   { } | ||
|  | 
 | ||
|  | // Insertor applies underlying manipulator function to stream
 | ||
|  | template<typename T1, typename T2> | ||
|  |   inline gzofstream& | ||
|  |   operator<<(gzofstream& s, const gzomanip2<T1,T2>& m) | ||
|  |   { return (*m.func)(s, m.val1, m.val2); } | ||
|  | 
 | ||
|  | // Insert this onto stream to simplify setting of compression level
 | ||
|  | inline gzomanip2<int,int> | ||
|  | setcompression(int l, int s = Z_DEFAULT_STRATEGY) | ||
|  | { return gzomanip2<int,int>(&setcompression, l, s); } | ||
|  | 
 | ||
|  | #endif // ZFSTREAM_H
 |