1 /**
2  * @file    bzfstream.h
3  * @brief   C++ I/O streams interface to the bzlib bz* functions
4  * @author  Akiya Jouraku
5  *
6  * <!--------------------------------------------------------------------------
7  * This file is part of libSBML.  Please visit http://sbml.org for more
8  * information about SBML, and the latest version of libSBML.
9  *
10  * Copyright (C) 2020 jointly by the following organizations:
11  *     1. California Institute of Technology, Pasadena, CA, USA
12  *     2. University of Heidelberg, Heidelberg, Germany
13  *     3. University College London, London, UK
14  *
15  * Copyright (C) 2019 jointly by the following organizations:
16  *     1. California Institute of Technology, Pasadena, CA, USA
17  *     2. University of Heidelberg, Heidelberg, Germany
18  *
19  * Copyright (C) 2013-2018 jointly by the following organizations:
20  *     1. California Institute of Technology, Pasadena, CA, USA
21  *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
22  *     3. University of Heidelberg, Heidelberg, Germany
23  *
24  * Copyright (C) 2009-2013 jointly by the following organizations:
25  *     1. California Institute of Technology, Pasadena, CA, USA
26  *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
27  *
28  * Copyright (C) 2006-2008 by the California Institute of Technology,
29  *     Pasadena, CA, USA
30  *
31  * Copyright (C) 2002-2005 jointly by the following organizations:
32  *     1. California Institute of Technology, Pasadena, CA, USA
33  *     2. Japan Science and Technology Agency, Japan
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the GNU Lesser General Public License as published by
37  * the Free Software Foundation.  A copy of the license agreement is provided
38  * in the file named "LICENSE.txt" included with this software distribution
39  * and also available online as http://sbml.org/software/libsbml/license.html
40  * ------------------------------------------------------------------------- -->
41  *
42  * Most of the code (except for bzip2 specific code) is based on zfstream.h
43  * implemented by Ludwig Schwardt <schwardt@sun.ac.za>
44  * original version by Kevin Ruland <kevin@rodin.wustl.edu>
45  * zfstream.h is contained in the contributed samples in zlib version 1.2.3
46  * (http://www.zlib.net).
47  *
48  * ---------------------------------------------------------------------- -->*/
49 
50 
51 #ifndef BZFSTREAM_H
52 #define BZFSTREAM_H
53 
54 #include <istream>  // not iostream, since we don't need cin/cout
55 #include <ostream>
56 #include "bzlib.h"
57 
58 /*****************************************************************************/
59 
60 /**
61  *  @brief  bzip2 file stream buffer class.
62  *
63  *  This class implements basic_filebuf for bzip2 files. It doesn't yet support
64  *  seeking (allowed by zlib but slow/limited), putback and read/write access
65  *  (tricky). Otherwise, it attempts to be a drop-in replacement for the standard
66  *  file streambuf.
67 */
68 class bzfilebuf : public std::streambuf
69 {
70 public:
71   //  Default constructor.
72   bzfilebuf();
73 
74   //  Destructor.
75   virtual
76   ~bzfilebuf();
77 
78   /**
79    *  @brief  Check if file is open.
80    *  @return  True if file is open.
81   */
82   bool
is_open()83   is_open() const { return (file != NULL); }
84 
85   /**
86    *  @brief  Open bzip2 file.
87    *  @param  name  File name.
88    *  @param  mode  Open mode flags.
89    *  @return  @c this on success, NULL on failure.
90   */
91   bzfilebuf*
92   open(const char* name,
93        std::ios_base::openmode mode);
94 
95   /**
96    *  @brief  Attach to already open bzip2 file.
97    *  @param  fd  File descriptor.
98    *  @param  mode  Open mode flags.
99    *  @return  @c this on success, NULL on failure.
100   */
101   bzfilebuf*
102   attach(int fd,
103          std::ios_base::openmode mode);
104 
105   /**
106    *  @brief  Close bzip2 file.
107    *  @return  @c this on success, NULL on failure.
108   */
109   bzfilebuf*
110   close();
111 
112 protected:
113   /**
114    *  @brief  Convert ios open mode int to mode string used by zlib.
115    *  @return  True if valid mode flag combination.
116   */
117   bool
118   open_mode(std::ios_base::openmode mode,
119             char* c_mode) const;
120 
121   /**
122    *  @brief  Number of characters available in stream buffer.
123    *  @return  Number of characters.
124    *
125    *  This indicates number of characters in get area of stream buffer.
126    *  These characters can be read without accessing the bzip2 file.
127   */
128   virtual std::streamsize
129   showmanyc();
130 
131   /**
132    *  @brief  Fill get area from bzip2 file.
133    *  @return  First character in get area on success, EOF on error.
134    *
135    *  This actually reads characters from bzip2 file to stream
136    *  buffer. Always buffered.
137   */
138   virtual int_type
139   underflow();
140 
141   /**
142    *  @brief  Write put area to bzip2 file.
143    *  @param  c  Extra character to add to buffer contents.
144    *  @return  Non-EOF on success, EOF on error.
145    *
146    *  This actually writes characters in stream buffer to
147    *  bzip2 file. With unbuffered output this is done one
148    *  character at a time.
149   */
150   virtual int_type
151   overflow(int_type c = std::streambuf::traits_type::eof());
152 
153   /**
154    *  @brief  Installs external stream buffer.
155    *  @param  p  Pointer to char buffer.
156    *  @param  n  Size of external buffer.
157    *  @return  @c this on success, NULL on failure.
158    *
159    *  Call setbuf(0,0) to enable unbuffered output.
160   */
161   virtual std::streambuf*
162   setbuf(char_type* p,
163          std::streamsize n);
164 
165   /**
166    *  @brief  Flush stream buffer to file.
167    *  @return  0 on success, -1 on error.
168    *
169    *  This calls underflow(EOF) to do the job.
170   */
171   virtual int
172   sync();
173 
174 //
175 // Some future enhancements
176 //
177 //  virtual int_type uflow();
178 //  virtual int_type pbackfail(int_type c = traits_type::eof());
179 //  virtual pos_type
180 //  seekoff(off_type off,
181 //          std::ios_base::seekdir way,
182 //          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);
183 //  virtual pos_type
184 //  seekpos(pos_type sp,
185 //          std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out);
186 
187 private:
188   /**
189    *  @brief  Allocate internal buffer.
190    *
191    *  This function is safe to call multiple times. It will ensure
192    *  that a proper internal buffer exists if it is required. If the
193    *  buffer already exists or is external, the buffer pointers will be
194    *  reset to their original state.
195   */
196   void
197   enable_buffer();
198 
199   /**
200    *  @brief  Destroy internal buffer.
201    *
202    *  This function is safe to call multiple times. It will ensure
203    *  that the internal buffer is deallocated if it exists. In any
204    *  case, it will also reset the buffer pointers.
205   */
206   void
207   disable_buffer();
208 
209   /**
210    *  Underlying file pointer.
211   */
212   BZFILE* file;
213 
214   /**
215    *  Mode in which file was opened.
216   */
217   std::ios_base::openmode io_mode;
218 
219   /**
220    *  @brief  True if this object owns file descriptor.
221    *
222    *  This makes the class responsible for closing the file
223    *  upon destruction.
224   */
225   bool own_fd;
226 
227   /**
228    *  @brief  Stream buffer.
229    *
230    *  For simplicity this remains allocated on the free store for the
231    *  entire life span of the bzfilebuf object, unless replaced by setbuf.
232   */
233   char_type* buffer;
234 
235   /**
236    *  @brief  Stream buffer size.
237    *
238    *  Defaults to system default buffer size (typically 8192 bytes).
239    *  Modified by setbuf.
240   */
241   std::streamsize buffer_size;
242 
243   /**
244    *  @brief  True if this object owns stream buffer.
245    *
246    *  This makes the class responsible for deleting the buffer
247    *  upon destruction.
248   */
249   bool own_buffer;
250 };
251 
252 /*****************************************************************************/
253 
254 /**
255  *  @brief  bzip2 file input stream class.
256  *
257  *  This class implements ifstream for bzip2 files. Seeking and putback
258  *  is not supported yet.
259 */
260 class bzifstream : public std::istream
261 {
262 public:
263   //  Default constructor
264   bzifstream();
265 
266   /**
267    *  @brief  Construct stream on bzip2 file to be opened.
268    *  @param  name  File name.
269    *  @param  mode  Open mode flags (forced to contain ios::in).
270   */
271   explicit
272   bzifstream(const char* name,
273              std::ios_base::openmode mode = std::ios_base::in);
274 
275   /**
276    *  @brief  Construct stream on already open bzip2 file.
277    *  @param  fd    File descriptor.
278    *  @param  mode  Open mode flags (forced to contain ios::in).
279   */
280   explicit
281   bzifstream(int fd,
282              std::ios_base::openmode mode = std::ios_base::in);
283 
284   /**
285    *  Obtain underlying stream buffer.
286   */
287   bzfilebuf*
rdbuf()288   rdbuf() const
289   { return const_cast<bzfilebuf*>(&sb); }
290 
291   /**
292    *  @brief  Check if file is open.
293    *  @return  True if file is open.
294   */
295   bool
is_open()296   is_open() { return sb.is_open(); }
297 
298   /**
299    *  @brief  Open bzip2 file.
300    *  @param  name  File name.
301    *  @param  mode  Open mode flags (forced to contain ios::in).
302    *
303    *  Stream will be in state good() if file opens successfully;
304    *  otherwise in state fail(). This differs from the behavior of
305    *  ifstream, which never sets the state to good() and therefore
306    *  won't allow you to reuse the stream for a second file unless
307    *  you manually clear() the state. The choice is a matter of
308    *  convenience.
309   */
310   void
311   open(const char* name,
312        std::ios_base::openmode mode = std::ios_base::in);
313 
314   /**
315    *  @brief  Attach to already open bzip2 file.
316    *  @param  fd  File descriptor.
317    *  @param  mode  Open mode flags (forced to contain ios::in).
318    *
319    *  Stream will be in state good() if attach succeeded; otherwise
320    *  in state fail().
321   */
322   void
323   attach(int fd,
324          std::ios_base::openmode mode = std::ios_base::in);
325 
326   /**
327    *  @brief  Close bzip2 file.
328    *
329    *  Stream will be in state fail() if close failed.
330   */
331   void
332   close();
333 
334 private:
335   /**
336    *  Underlying stream buffer.
337   */
338   bzfilebuf sb;
339 };
340 
341 /*****************************************************************************/
342 
343 /**
344  *  @brief  Gzipped file output stream class.
345  *
346  *  This class implements ofstream for bzip2 files. Seeking and putback
347  *  is not supported yet.
348 */
349 class bzofstream : public std::ostream
350 {
351 public:
352   //  Default constructor
353   bzofstream();
354 
355   /**
356    *  @brief  Construct stream on bzip2 file to be opened.
357    *  @param  name  File name.
358    *  @param  mode  Open mode flags (forced to contain ios::out).
359   */
360   explicit
361   bzofstream(const char* name,
362              std::ios_base::openmode mode = std::ios_base::out);
363 
364   /**
365    *  @brief  Construct stream on already open bzip2 file.
366    *  @param  fd    File descriptor.
367    *  @param  mode  Open mode flags (forced to contain ios::out).
368   */
369   explicit
370   bzofstream(int fd,
371              std::ios_base::openmode mode = std::ios_base::out);
372 
373   /**
374    *  Obtain underlying stream buffer.
375   */
376   bzfilebuf*
rdbuf()377   rdbuf() const
378   { return const_cast<bzfilebuf*>(&sb); }
379 
380   /**
381    *  @brief  Check if file is open.
382    *  @return  True if file is open.
383   */
384   bool
is_open()385   is_open() { return sb.is_open(); }
386 
387   /**
388    *  @brief  Open bzip2 file.
389    *  @param  name  File name.
390    *  @param  mode  Open mode flags (forced to contain ios::out).
391    *
392    *  Stream will be in state good() if file opens successfully;
393    *  otherwise in state fail(). This differs from the behavior of
394    *  ofstream, which never sets the state to good() and therefore
395    *  won't allow you to reuse the stream for a second file unless
396    *  you manually clear() the state. The choice is a matter of
397    *  convenience.
398   */
399   void
400   open(const char* name,
401        std::ios_base::openmode mode = std::ios_base::out);
402 
403   /**
404    *  @brief  Attach to already open bzip2 file.
405    *  @param  fd  File descriptor.
406    *  @param  mode  Open mode flags (forced to contain ios::out).
407    *
408    *  Stream will be in state good() if attach succeeded; otherwise
409    *  in state fail().
410   */
411   void
412   attach(int fd,
413          std::ios_base::openmode mode = std::ios_base::out);
414 
415   /**
416    *  @brief  Close bzip2 file.
417    *
418    *  Stream will be in state fail() if close failed.
419   */
420   void
421   close();
422 
423 private:
424   /**
425    *  Underlying stream buffer.
426   */
427   bzfilebuf sb;
428 };
429 
430 /*****************************************************************************/
431 
432 /**
433  *  @brief  Gzipped file output stream manipulator class.
434  *
435  *  This class defines a two-argument manipulator for bzofstream. It is used
436  *  as base for the setcompression(int,int) manipulator.
437 */
438 template<typename T1, typename T2>
439   class bzomanip2
440   {
441   public:
442     // Allows insertor to peek at internals
443     template <typename Ta, typename Tb>
444       friend bzofstream&
445       operator<<(bzofstream&,
446                  const bzomanip2<Ta,Tb>&);
447 
448     // Constructor
449     bzomanip2(bzofstream& (*f)(bzofstream&, T1, T2),
450               T1 v1,
451               T2 v2);
452   private:
453     // Underlying manipulator function
454     bzofstream&
455     (*func)(bzofstream&, T1, T2);
456 
457     // Arguments for manipulator function
458     T1 val1;
459     T2 val2;
460   };
461 
462 /*****************************************************************************/
463 
464 
465 // Manipulator constructor stores arguments
466 template<typename T1, typename T2>
467   inline
bzomanip2(bzofstream & (* f)(bzofstream &,T1,T2),T1 v1,T2 v2)468   bzomanip2<T1,T2>::bzomanip2(bzofstream &(*f)(bzofstream &, T1, T2),
469                               T1 v1,
470                               T2 v2)
471   : func(f), val1(v1), val2(v2)
472   { }
473 
474 // Insertor applies underlying manipulator function to stream
475 template<typename T1, typename T2>
476   inline bzofstream&
477   operator<<(bzofstream& s, const bzomanip2<T1,T2>& m)
478   { return (*m.func)(s, m.val1, m.val2); }
479 
480 
481 #endif // BZFSTREAM_H
482