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