1 /* 2 * A C++ I/O streams interface to the zlib gz* functions 3 * 4 * by Ludwig Schwardt <schwardt@sun.ac.za> 5 * original version by Kevin Ruland <kevin@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