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
is_open()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*
rdbuf()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
is_open()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*
rdbuf()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
is_open()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
gzomanip2(gzofstream & (* f)(gzofstream &,T1,T2),T1 v1,T2 v2)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
467