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