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*/
27class gzfilebuf : public std::streambuf
28{
29public:
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
86protected:
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
161private:
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*/
234class gzifstream : public std::istream
235{
236public:
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
308private:
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*/
323class gzofstream : public std::ostream
324{
325public:
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
397private:
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*/
412template<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
439inline gzofstream&
440setcompression(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
447template<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
456template<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
462inline gzomanip2<int,int>
463setcompression(int l, int s = Z_DEFAULT_STRATEGY)
464{ return gzomanip2<int,int>(&setcompression, l, s); }
465
466#endif // ZFSTREAM_H
467