cannam@89: /* cannam@89: * A C++ I/O streams interface to the zlib gz* functions cannam@89: * cannam@89: * by Ludwig Schwardt cannam@89: * original version by Kevin Ruland cannam@89: * cannam@89: * This version is standard-compliant and compatible with gcc 3.x. cannam@89: */ cannam@89: cannam@89: #ifndef ZFSTREAM_H cannam@89: #define ZFSTREAM_H cannam@89: cannam@89: #include // not iostream, since we don't need cin/cout cannam@89: #include cannam@89: #include "zlib.h" cannam@89: cannam@89: /*****************************************************************************/ cannam@89: cannam@89: /** cannam@89: * @brief Gzipped file stream buffer class. cannam@89: * cannam@89: * This class implements basic_filebuf for gzipped files. It doesn't yet support cannam@89: * seeking (allowed by zlib but slow/limited), putback and read/write access cannam@89: * (tricky). Otherwise, it attempts to be a drop-in replacement for the standard cannam@89: * file streambuf. cannam@89: */ cannam@89: class gzfilebuf : public std::streambuf cannam@89: { cannam@89: public: cannam@89: // Default constructor. cannam@89: gzfilebuf(); cannam@89: cannam@89: // Destructor. cannam@89: virtual cannam@89: ~gzfilebuf(); cannam@89: cannam@89: /** cannam@89: * @brief Set compression level and strategy on the fly. cannam@89: * @param comp_level Compression level (see zlib.h for allowed values) cannam@89: * @param comp_strategy Compression strategy (see zlib.h for allowed values) cannam@89: * @return Z_OK on success, Z_STREAM_ERROR otherwise. cannam@89: * cannam@89: * Unfortunately, these parameters cannot be modified separately, as the cannam@89: * previous zfstream version assumed. Since the strategy is seldom changed, cannam@89: * it can default and setcompression(level) then becomes like the old cannam@89: * setcompressionlevel(level). cannam@89: */ cannam@89: int cannam@89: setcompression(int comp_level, cannam@89: int comp_strategy = Z_DEFAULT_STRATEGY); cannam@89: cannam@89: /** cannam@89: * @brief Check if file is open. cannam@89: * @return True if file is open. cannam@89: */ cannam@89: bool cannam@89: is_open() const { return (file != NULL); } cannam@89: cannam@89: /** cannam@89: * @brief Open gzipped file. cannam@89: * @param name File name. cannam@89: * @param mode Open mode flags. cannam@89: * @return @c this on success, NULL on failure. cannam@89: */ cannam@89: gzfilebuf* cannam@89: open(const char* name, cannam@89: std::ios_base::openmode mode); cannam@89: cannam@89: /** cannam@89: * @brief Attach to already open gzipped file. cannam@89: * @param fd File descriptor. cannam@89: * @param mode Open mode flags. cannam@89: * @return @c this on success, NULL on failure. cannam@89: */ cannam@89: gzfilebuf* cannam@89: attach(int fd, cannam@89: std::ios_base::openmode mode); cannam@89: cannam@89: /** cannam@89: * @brief Close gzipped file. cannam@89: * @return @c this on success, NULL on failure. cannam@89: */ cannam@89: gzfilebuf* cannam@89: close(); cannam@89: cannam@89: protected: cannam@89: /** cannam@89: * @brief Convert ios open mode int to mode string used by zlib. cannam@89: * @return True if valid mode flag combination. cannam@89: */ cannam@89: bool cannam@89: open_mode(std::ios_base::openmode mode, cannam@89: char* c_mode) const; cannam@89: cannam@89: /** cannam@89: * @brief Number of characters available in stream buffer. cannam@89: * @return Number of characters. cannam@89: * cannam@89: * This indicates number of characters in get area of stream buffer. cannam@89: * These characters can be read without accessing the gzipped file. cannam@89: */ cannam@89: virtual std::streamsize cannam@89: showmanyc(); cannam@89: cannam@89: /** cannam@89: * @brief Fill get area from gzipped file. cannam@89: * @return First character in get area on success, EOF on error. cannam@89: * cannam@89: * This actually reads characters from gzipped file to stream cannam@89: * buffer. Always buffered. cannam@89: */ cannam@89: virtual int_type cannam@89: underflow(); cannam@89: cannam@89: /** cannam@89: * @brief Write put area to gzipped file. cannam@89: * @param c Extra character to add to buffer contents. cannam@89: * @return Non-EOF on success, EOF on error. cannam@89: * cannam@89: * This actually writes characters in stream buffer to cannam@89: * gzipped file. With unbuffered output this is done one cannam@89: * character at a time. cannam@89: */ cannam@89: virtual int_type cannam@89: overflow(int_type c = traits_type::eof()); cannam@89: cannam@89: /** cannam@89: * @brief Installs external stream buffer. cannam@89: * @param p Pointer to char buffer. cannam@89: * @param n Size of external buffer. cannam@89: * @return @c this on success, NULL on failure. cannam@89: * cannam@89: * Call setbuf(0,0) to enable unbuffered output. cannam@89: */ cannam@89: virtual std::streambuf* cannam@89: setbuf(char_type* p, cannam@89: std::streamsize n); cannam@89: cannam@89: /** cannam@89: * @brief Flush stream buffer to file. cannam@89: * @return 0 on success, -1 on error. cannam@89: * cannam@89: * This calls underflow(EOF) to do the job. cannam@89: */ cannam@89: virtual int cannam@89: sync(); cannam@89: cannam@89: // cannam@89: // Some future enhancements cannam@89: // cannam@89: // virtual int_type uflow(); cannam@89: // virtual int_type pbackfail(int_type c = traits_type::eof()); cannam@89: // virtual pos_type cannam@89: // seekoff(off_type off, cannam@89: // std::ios_base::seekdir way, cannam@89: // std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out); cannam@89: // virtual pos_type cannam@89: // seekpos(pos_type sp, cannam@89: // std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out); cannam@89: cannam@89: private: cannam@89: /** cannam@89: * @brief Allocate internal buffer. cannam@89: * cannam@89: * This function is safe to call multiple times. It will ensure cannam@89: * that a proper internal buffer exists if it is required. If the cannam@89: * buffer already exists or is external, the buffer pointers will be cannam@89: * reset to their original state. cannam@89: */ cannam@89: void cannam@89: enable_buffer(); cannam@89: cannam@89: /** cannam@89: * @brief Destroy internal buffer. cannam@89: * cannam@89: * This function is safe to call multiple times. It will ensure cannam@89: * that the internal buffer is deallocated if it exists. In any cannam@89: * case, it will also reset the buffer pointers. cannam@89: */ cannam@89: void cannam@89: disable_buffer(); cannam@89: cannam@89: /** cannam@89: * Underlying file pointer. cannam@89: */ cannam@89: gzFile file; cannam@89: cannam@89: /** cannam@89: * Mode in which file was opened. cannam@89: */ cannam@89: std::ios_base::openmode io_mode; cannam@89: cannam@89: /** cannam@89: * @brief True if this object owns file descriptor. cannam@89: * cannam@89: * This makes the class responsible for closing the file cannam@89: * upon destruction. cannam@89: */ cannam@89: bool own_fd; cannam@89: cannam@89: /** cannam@89: * @brief Stream buffer. cannam@89: * cannam@89: * For simplicity this remains allocated on the free store for the cannam@89: * entire life span of the gzfilebuf object, unless replaced by setbuf. cannam@89: */ cannam@89: char_type* buffer; cannam@89: cannam@89: /** cannam@89: * @brief Stream buffer size. cannam@89: * cannam@89: * Defaults to system default buffer size (typically 8192 bytes). cannam@89: * Modified by setbuf. cannam@89: */ cannam@89: std::streamsize buffer_size; cannam@89: cannam@89: /** cannam@89: * @brief True if this object owns stream buffer. cannam@89: * cannam@89: * This makes the class responsible for deleting the buffer cannam@89: * upon destruction. cannam@89: */ cannam@89: bool own_buffer; cannam@89: }; cannam@89: cannam@89: /*****************************************************************************/ cannam@89: cannam@89: /** cannam@89: * @brief Gzipped file input stream class. cannam@89: * cannam@89: * This class implements ifstream for gzipped files. Seeking and putback cannam@89: * is not supported yet. cannam@89: */ cannam@89: class gzifstream : public std::istream cannam@89: { cannam@89: public: cannam@89: // Default constructor cannam@89: gzifstream(); cannam@89: cannam@89: /** cannam@89: * @brief Construct stream on gzipped file to be opened. cannam@89: * @param name File name. cannam@89: * @param mode Open mode flags (forced to contain ios::in). cannam@89: */ cannam@89: explicit cannam@89: gzifstream(const char* name, cannam@89: std::ios_base::openmode mode = std::ios_base::in); cannam@89: cannam@89: /** cannam@89: * @brief Construct stream on already open gzipped file. cannam@89: * @param fd File descriptor. cannam@89: * @param mode Open mode flags (forced to contain ios::in). cannam@89: */ cannam@89: explicit cannam@89: gzifstream(int fd, cannam@89: std::ios_base::openmode mode = std::ios_base::in); cannam@89: cannam@89: /** cannam@89: * Obtain underlying stream buffer. cannam@89: */ cannam@89: gzfilebuf* cannam@89: rdbuf() const cannam@89: { return const_cast(&sb); } cannam@89: cannam@89: /** cannam@89: * @brief Check if file is open. cannam@89: * @return True if file is open. cannam@89: */ cannam@89: bool cannam@89: is_open() { return sb.is_open(); } cannam@89: cannam@89: /** cannam@89: * @brief Open gzipped file. cannam@89: * @param name File name. cannam@89: * @param mode Open mode flags (forced to contain ios::in). cannam@89: * cannam@89: * Stream will be in state good() if file opens successfully; cannam@89: * otherwise in state fail(). This differs from the behavior of cannam@89: * ifstream, which never sets the state to good() and therefore cannam@89: * won't allow you to reuse the stream for a second file unless cannam@89: * you manually clear() the state. The choice is a matter of cannam@89: * convenience. cannam@89: */ cannam@89: void cannam@89: open(const char* name, cannam@89: std::ios_base::openmode mode = std::ios_base::in); cannam@89: cannam@89: /** cannam@89: * @brief Attach to already open gzipped file. cannam@89: * @param fd File descriptor. cannam@89: * @param mode Open mode flags (forced to contain ios::in). cannam@89: * cannam@89: * Stream will be in state good() if attach succeeded; otherwise cannam@89: * in state fail(). cannam@89: */ cannam@89: void cannam@89: attach(int fd, cannam@89: std::ios_base::openmode mode = std::ios_base::in); cannam@89: cannam@89: /** cannam@89: * @brief Close gzipped file. cannam@89: * cannam@89: * Stream will be in state fail() if close failed. cannam@89: */ cannam@89: void cannam@89: close(); cannam@89: cannam@89: private: cannam@89: /** cannam@89: * Underlying stream buffer. cannam@89: */ cannam@89: gzfilebuf sb; cannam@89: }; cannam@89: cannam@89: /*****************************************************************************/ cannam@89: cannam@89: /** cannam@89: * @brief Gzipped file output stream class. cannam@89: * cannam@89: * This class implements ofstream for gzipped files. Seeking and putback cannam@89: * is not supported yet. cannam@89: */ cannam@89: class gzofstream : public std::ostream cannam@89: { cannam@89: public: cannam@89: // Default constructor cannam@89: gzofstream(); cannam@89: cannam@89: /** cannam@89: * @brief Construct stream on gzipped file to be opened. cannam@89: * @param name File name. cannam@89: * @param mode Open mode flags (forced to contain ios::out). cannam@89: */ cannam@89: explicit cannam@89: gzofstream(const char* name, cannam@89: std::ios_base::openmode mode = std::ios_base::out); cannam@89: cannam@89: /** cannam@89: * @brief Construct stream on already open gzipped file. cannam@89: * @param fd File descriptor. cannam@89: * @param mode Open mode flags (forced to contain ios::out). cannam@89: */ cannam@89: explicit cannam@89: gzofstream(int fd, cannam@89: std::ios_base::openmode mode = std::ios_base::out); cannam@89: cannam@89: /** cannam@89: * Obtain underlying stream buffer. cannam@89: */ cannam@89: gzfilebuf* cannam@89: rdbuf() const cannam@89: { return const_cast(&sb); } cannam@89: cannam@89: /** cannam@89: * @brief Check if file is open. cannam@89: * @return True if file is open. cannam@89: */ cannam@89: bool cannam@89: is_open() { return sb.is_open(); } cannam@89: cannam@89: /** cannam@89: * @brief Open gzipped file. cannam@89: * @param name File name. cannam@89: * @param mode Open mode flags (forced to contain ios::out). cannam@89: * cannam@89: * Stream will be in state good() if file opens successfully; cannam@89: * otherwise in state fail(). This differs from the behavior of cannam@89: * ofstream, which never sets the state to good() and therefore cannam@89: * won't allow you to reuse the stream for a second file unless cannam@89: * you manually clear() the state. The choice is a matter of cannam@89: * convenience. cannam@89: */ cannam@89: void cannam@89: open(const char* name, cannam@89: std::ios_base::openmode mode = std::ios_base::out); cannam@89: cannam@89: /** cannam@89: * @brief Attach to already open gzipped file. cannam@89: * @param fd File descriptor. cannam@89: * @param mode Open mode flags (forced to contain ios::out). cannam@89: * cannam@89: * Stream will be in state good() if attach succeeded; otherwise cannam@89: * in state fail(). cannam@89: */ cannam@89: void cannam@89: attach(int fd, cannam@89: std::ios_base::openmode mode = std::ios_base::out); cannam@89: cannam@89: /** cannam@89: * @brief Close gzipped file. cannam@89: * cannam@89: * Stream will be in state fail() if close failed. cannam@89: */ cannam@89: void cannam@89: close(); cannam@89: cannam@89: private: cannam@89: /** cannam@89: * Underlying stream buffer. cannam@89: */ cannam@89: gzfilebuf sb; cannam@89: }; cannam@89: cannam@89: /*****************************************************************************/ cannam@89: cannam@89: /** cannam@89: * @brief Gzipped file output stream manipulator class. cannam@89: * cannam@89: * This class defines a two-argument manipulator for gzofstream. It is used cannam@89: * as base for the setcompression(int,int) manipulator. cannam@89: */ cannam@89: template cannam@89: class gzomanip2 cannam@89: { cannam@89: public: cannam@89: // Allows insertor to peek at internals cannam@89: template cannam@89: friend gzofstream& cannam@89: operator<<(gzofstream&, cannam@89: const gzomanip2&); cannam@89: cannam@89: // Constructor cannam@89: gzomanip2(gzofstream& (*f)(gzofstream&, T1, T2), cannam@89: T1 v1, cannam@89: T2 v2); cannam@89: private: cannam@89: // Underlying manipulator function cannam@89: gzofstream& cannam@89: (*func)(gzofstream&, T1, T2); cannam@89: cannam@89: // Arguments for manipulator function cannam@89: T1 val1; cannam@89: T2 val2; cannam@89: }; cannam@89: cannam@89: /*****************************************************************************/ cannam@89: cannam@89: // Manipulator function thunks through to stream buffer cannam@89: inline gzofstream& cannam@89: setcompression(gzofstream &gzs, int l, int s = Z_DEFAULT_STRATEGY) cannam@89: { cannam@89: (gzs.rdbuf())->setcompression(l, s); cannam@89: return gzs; cannam@89: } cannam@89: cannam@89: // Manipulator constructor stores arguments cannam@89: template cannam@89: inline cannam@89: gzomanip2::gzomanip2(gzofstream &(*f)(gzofstream &, T1, T2), cannam@89: T1 v1, cannam@89: T2 v2) cannam@89: : func(f), val1(v1), val2(v2) cannam@89: { } cannam@89: cannam@89: // Insertor applies underlying manipulator function to stream cannam@89: template cannam@89: inline gzofstream& cannam@89: operator<<(gzofstream& s, const gzomanip2& m) cannam@89: { return (*m.func)(s, m.val1, m.val2); } cannam@89: cannam@89: // Insert this onto stream to simplify setting of compression level cannam@89: inline gzomanip2 cannam@89: setcompression(int l, int s = Z_DEFAULT_STRATEGY) cannam@89: { return gzomanip2(&setcompression, l, s); } cannam@89: cannam@89: #endif // ZFSTREAM_H