zfstream.cc

Go to the documentation of this file.
00001 /*
00002  * A C++ I/O streams interface to the zlib gz* functions
00003  *
00004  * by Ludwig Schwardt <schwardt@sun.ac.za>
00005  * original version by Kevin Ruland <kevin@rodin.wustl.edu>
00006  *
00007  * This version is standard-compliant and compatible with gcc 3.x.
00008  */
00009 
00010 #include "zfstream.h"
00011 #include <cstring>          // for strcpy, strcat, strlen (mode strings)
00012 #include <cstdio>           // for BUFSIZ
00013 
00014 // Internal buffer sizes (default and "unbuffered" versions)
00015 #define BIGBUFSIZE BUFSIZ
00016 #define SMALLBUFSIZE 1
00017 
00018 /*****************************************************************************/
00019 
00020 // Default constructor
00021 gzfilebuf::gzfilebuf()
00022 : file(NULL), io_mode(std::ios_base::openmode(0)), own_fd(false),
00023   buffer(NULL), buffer_size(BIGBUFSIZE), own_buffer(true)
00024 {
00025   // No buffers to start with
00026   this->disable_buffer();
00027 }
00028 
00029 // Destructor
00030 gzfilebuf::~gzfilebuf()
00031 {
00032   // Sync output buffer and close only if responsible for file
00033   // (i.e. attached streams should be left open at this stage)
00034   this->sync();
00035   if (own_fd)
00036     this->close();
00037   // Make sure internal buffer is deallocated
00038   this->disable_buffer();
00039 }
00040 
00041 // Set compression level and strategy
00042 int
00043 gzfilebuf::setcompression(int comp_level,
00044                           int comp_strategy)
00045 {
00046   return gzsetparams(file, comp_level, comp_strategy);
00047 }
00048 
00049 // Open gzipped file
00050 gzfilebuf*
00051 gzfilebuf::open(const char *name,
00052                 std::ios_base::openmode mode)
00053 {
00054   // Fail if file already open
00055   if (this->is_open())
00056     return NULL;
00057   // Don't support simultaneous read/write access (yet)
00058   if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
00059     return NULL;
00060 
00061   // Build mode string for gzopen and check it [27.8.1.3.2]
00062   char char_mode[6] = "\0\0\0\0\0";
00063   if (!this->open_mode(mode, char_mode))
00064     return NULL;
00065 
00066   // Attempt to open file
00067   if ((file = gzopen(name, char_mode)) == NULL)
00068     return NULL;
00069 
00070   // On success, allocate internal buffer and set flags
00071   this->enable_buffer();
00072   io_mode = mode;
00073   own_fd = true;
00074   return this;
00075 }
00076 
00077 // Attach to gzipped file
00078 gzfilebuf*
00079 gzfilebuf::attach(int fd,
00080                   std::ios_base::openmode mode)
00081 {
00082   // Fail if file already open
00083   if (this->is_open())
00084     return NULL;
00085   // Don't support simultaneous read/write access (yet)
00086   if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
00087     return NULL;
00088 
00089   // Build mode string for gzdopen and check it [27.8.1.3.2]
00090   char char_mode[6] = "\0\0\0\0\0";
00091   if (!this->open_mode(mode, char_mode))
00092     return NULL;
00093 
00094   // Attempt to attach to file
00095   if ((file = gzdopen(fd, char_mode)) == NULL)
00096     return NULL;
00097 
00098   // On success, allocate internal buffer and set flags
00099   this->enable_buffer();
00100   io_mode = mode;
00101   own_fd = false;
00102   return this;
00103 }
00104 
00105 // Close gzipped file
00106 gzfilebuf*
00107 gzfilebuf::close()
00108 {
00109   // Fail immediately if no file is open
00110   if (!this->is_open())
00111     return NULL;
00112   // Assume success
00113   gzfilebuf* retval = this;
00114   // Attempt to sync and close gzipped file
00115   if (this->sync() == -1)
00116     retval = NULL;
00117   if (gzclose(file) < 0)
00118     retval = NULL;
00119   // File is now gone anyway (postcondition [27.8.1.3.8])
00120   file = NULL;
00121   own_fd = false;
00122   // Destroy internal buffer if it exists
00123   this->disable_buffer();
00124   return retval;
00125 }
00126 
00127 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00128 
00129 // Convert int open mode to mode string
00130 bool
00131 gzfilebuf::open_mode(std::ios_base::openmode mode,
00132                      char* c_mode) const
00133 {
00134   bool testb = mode & std::ios_base::binary;
00135   bool testi = mode & std::ios_base::in;
00136   bool testo = mode & std::ios_base::out;
00137   bool testt = mode & std::ios_base::trunc;
00138   bool testa = mode & std::ios_base::app;
00139 
00140   // Check for valid flag combinations - see [27.8.1.3.2] (Table 92)
00141   // Original zfstream hardcoded the compression level to maximum here...
00142   // Double the time for less than 1% size improvement seems
00143   // excessive though - keeping it at the default level
00144   // To change back, just append "9" to the next three mode strings
00145   if (!testi && testo && !testt && !testa)
00146     strcpy(c_mode, "w");
00147   if (!testi && testo && !testt && testa)
00148     strcpy(c_mode, "a");
00149   if (!testi && testo && testt && !testa)
00150     strcpy(c_mode, "w");
00151   if (testi && !testo && !testt && !testa)
00152     strcpy(c_mode, "r");
00153   // No read/write mode yet
00154 //  if (testi && testo && !testt && !testa)
00155 //    strcpy(c_mode, "r+");
00156 //  if (testi && testo && testt && !testa)
00157 //    strcpy(c_mode, "w+");
00158 
00159   // Mode string should be empty for invalid combination of flags
00160   if (strlen(c_mode) == 0)
00161     return false;
00162   if (testb)
00163     strcat(c_mode, "b");
00164   return true;
00165 }
00166 
00167 // Determine number of characters in internal get buffer
00168 std::streamsize
00169 gzfilebuf::showmanyc()
00170 {
00171   // Calls to underflow will fail if file not opened for reading
00172   if (!this->is_open() || !(io_mode & std::ios_base::in))
00173     return -1;
00174   // Make sure get area is in use
00175   if (this->gptr() && (this->gptr() < this->egptr()))
00176     return std::streamsize(this->egptr() - this->gptr());
00177   else
00178     return 0;
00179 }
00180 
00181 // Fill get area from gzipped file
00182 gzfilebuf::int_type
00183 gzfilebuf::underflow()
00184 {
00185   // If something is left in the get area by chance, return it
00186   // (this shouldn't normally happen, as underflow is only supposed
00187   // to be called when gptr >= egptr, but it serves as error check)
00188   if (this->gptr() && (this->gptr() < this->egptr()))
00189     return traits_type::to_int_type(*(this->gptr()));
00190 
00191   // If the file hasn't been opened for reading, produce error
00192   if (!this->is_open() || !(io_mode & std::ios_base::in))
00193     return traits_type::eof();
00194 
00195   // Attempt to fill internal buffer from gzipped file
00196   // (buffer must be guaranteed to exist...)
00197   int bytes_read = gzread(file, buffer, buffer_size);
00198   // Indicates error or EOF
00199   if (bytes_read <= 0)
00200   {
00201     // Reset get area
00202     this->setg(buffer, buffer, buffer);
00203     return traits_type::eof();
00204   }
00205   // Make all bytes read from file available as get area
00206   this->setg(buffer, buffer, buffer + bytes_read);
00207 
00208   // Return next character in get area
00209   return traits_type::to_int_type(*(this->gptr()));
00210 }
00211 
00212 // Write put area to gzipped file
00213 gzfilebuf::int_type
00214 gzfilebuf::overflow(int_type c)
00215 {
00216   // Determine whether put area is in use
00217   if (this->pbase())
00218   {
00219     // Double-check pointer range
00220     if (this->pptr() > this->epptr() || this->pptr() < this->pbase())
00221       return traits_type::eof();
00222     // Add extra character to buffer if not EOF
00223     if (!traits_type::eq_int_type(c, traits_type::eof()))
00224     {
00225       *(this->pptr()) = traits_type::to_char_type(c);
00226       this->pbump(1);
00227     }
00228     // Number of characters to write to file
00229     int bytes_to_write = this->pptr() - this->pbase();
00230     // Overflow doesn't fail if nothing is to be written
00231     if (bytes_to_write > 0)
00232     {
00233       // If the file hasn't been opened for writing, produce error
00234       if (!this->is_open() || !(io_mode & std::ios_base::out))
00235         return traits_type::eof();
00236       // If gzipped file won't accept all bytes written to it, fail
00237       if (gzwrite(file, this->pbase(), bytes_to_write) != bytes_to_write)
00238         return traits_type::eof();
00239       // Reset next pointer to point to pbase on success
00240       this->pbump(-bytes_to_write);
00241     }
00242   }
00243   // Write extra character to file if not EOF
00244   else if (!traits_type::eq_int_type(c, traits_type::eof()))
00245   {
00246     // If the file hasn't been opened for writing, produce error
00247     if (!this->is_open() || !(io_mode & std::ios_base::out))
00248       return traits_type::eof();
00249     // Impromptu char buffer (allows "unbuffered" output)
00250     char_type last_char = traits_type::to_char_type(c);
00251     // If gzipped file won't accept this character, fail
00252     if (gzwrite(file, &last_char, 1) != 1)
00253       return traits_type::eof();
00254   }
00255 
00256   // If you got here, you have succeeded (even if c was EOF)
00257   // The return value should therefore be non-EOF
00258   if (traits_type::eq_int_type(c, traits_type::eof()))
00259     return traits_type::not_eof(c);
00260   else
00261     return c;
00262 }
00263 
00264 // Assign new buffer
00265 std::streambuf*
00266 gzfilebuf::setbuf(char_type* p,
00267                   std::streamsize n)
00268 {
00269   // First make sure stuff is sync'ed, for safety
00270   if (this->sync() == -1)
00271     return NULL;
00272   // If buffering is turned off on purpose via setbuf(0,0), still allocate one...
00273   // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
00274   // least a buffer of size 1 (very inefficient though, therefore make it bigger?)
00275   // This follows from [27.5.2.4.3]/12 (gptr needs to point at something, it seems)
00276   if (!p || !n)
00277   {
00278     // Replace existing buffer (if any) with small internal buffer
00279     this->disable_buffer();
00280     buffer = NULL;
00281     buffer_size = 0;
00282     own_buffer = true;
00283     this->enable_buffer();
00284   }
00285   else
00286   {
00287     // Replace existing buffer (if any) with external buffer
00288     this->disable_buffer();
00289     buffer = p;
00290     buffer_size = n;
00291     own_buffer = false;
00292     this->enable_buffer();
00293   }
00294   return this;
00295 }
00296 
00297 // Write put area to gzipped file (i.e. ensures that put area is empty)
00298 int
00299 gzfilebuf::sync()
00300 {
00301   return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0;
00302 }
00303 
00304 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00305 
00306 // Allocate internal buffer
00307 void
00308 gzfilebuf::enable_buffer()
00309 {
00310   // If internal buffer required, allocate one
00311   if (own_buffer && !buffer)
00312   {
00313     // Check for buffered vs. "unbuffered"
00314     if (buffer_size > 0)
00315     {
00316       // Allocate internal buffer
00317       buffer = new char_type[buffer_size];
00318       // Get area starts empty and will be expanded by underflow as need arises
00319       this->setg(buffer, buffer, buffer);
00320       // Setup entire internal buffer as put area.
00321       // The one-past-end pointer actually points to the last element of the buffer,
00322       // so that overflow(c) can safely add the extra character c to the sequence.
00323       // These pointers remain in place for the duration of the buffer
00324       this->setp(buffer, buffer + buffer_size - 1);
00325     }
00326     else
00327     {
00328       // Even in "unbuffered" case, (small?) get buffer is still required
00329       buffer_size = SMALLBUFSIZE;
00330       buffer = new char_type[buffer_size];
00331       this->setg(buffer, buffer, buffer);
00332       // "Unbuffered" means no put buffer
00333       this->setp(0, 0);
00334     }
00335   }
00336   else
00337   {
00338     // If buffer already allocated, reset buffer pointers just to make sure no
00339     // stale chars are lying around
00340     this->setg(buffer, buffer, buffer);
00341     this->setp(buffer, buffer + buffer_size - 1);
00342   }
00343 }
00344 
00345 // Destroy internal buffer
00346 void
00347 gzfilebuf::disable_buffer()
00348 {
00349   // If internal buffer exists, deallocate it
00350   if (own_buffer && buffer)
00351   {
00352     // Preserve unbuffered status by zeroing size
00353     if (!this->pbase())
00354       buffer_size = 0;
00355     delete[] buffer;
00356     buffer = NULL;
00357     this->setg(0, 0, 0);
00358     this->setp(0, 0);
00359   }
00360   else
00361   {
00362     // Reset buffer pointers to initial state if external buffer exists
00363     this->setg(buffer, buffer, buffer);
00364     if (buffer)
00365       this->setp(buffer, buffer + buffer_size - 1);
00366     else
00367       this->setp(0, 0);
00368   }
00369 }
00370 
00371 /*****************************************************************************/
00372 
00373 // Default constructor initializes stream buffer
00374 gzifstream::gzifstream()
00375 : std::istream(NULL), sb()
00376 { this->init(&sb); }
00377 
00378 // Initialize stream buffer and open file
00379 gzifstream::gzifstream(const char* name,
00380                        std::ios_base::openmode mode)
00381 : std::istream(NULL), sb()
00382 {
00383   this->init(&sb);
00384   this->open(name, mode);
00385 }
00386 
00387 // Initialize stream buffer and attach to file
00388 gzifstream::gzifstream(int fd,
00389                        std::ios_base::openmode mode)
00390 : std::istream(NULL), sb()
00391 {
00392   this->init(&sb);
00393   this->attach(fd, mode);
00394 }
00395 
00396 // Open file and go into fail() state if unsuccessful
00397 void
00398 gzifstream::open(const char* name,
00399                  std::ios_base::openmode mode)
00400 {
00401   if (!sb.open(name, mode | std::ios_base::in))
00402     this->setstate(std::ios_base::failbit);
00403   else
00404     this->clear();
00405 }
00406 
00407 // Attach to file and go into fail() state if unsuccessful
00408 void
00409 gzifstream::attach(int fd,
00410                    std::ios_base::openmode mode)
00411 {
00412   if (!sb.attach(fd, mode | std::ios_base::in))
00413     this->setstate(std::ios_base::failbit);
00414   else
00415     this->clear();
00416 }
00417 
00418 // Close file
00419 void
00420 gzifstream::close()
00421 {
00422   if (!sb.close())
00423     this->setstate(std::ios_base::failbit);
00424 }
00425 
00426 /*****************************************************************************/
00427 
00428 // Default constructor initializes stream buffer
00429 gzofstream::gzofstream()
00430 : std::ostream(NULL), sb()
00431 { this->init(&sb); }
00432 
00433 // Initialize stream buffer and open file
00434 gzofstream::gzofstream(const char* name,
00435                        std::ios_base::openmode mode)
00436 : std::ostream(NULL), sb()
00437 {
00438   this->init(&sb);
00439   this->open(name, mode);
00440 }
00441 
00442 // Initialize stream buffer and attach to file
00443 gzofstream::gzofstream(int fd,
00444                        std::ios_base::openmode mode)
00445 : std::ostream(NULL), sb()
00446 {
00447   this->init(&sb);
00448   this->attach(fd, mode);
00449 }
00450 
00451 // Open file and go into fail() state if unsuccessful
00452 void
00453 gzofstream::open(const char* name,
00454                  std::ios_base::openmode mode)
00455 {
00456   if (!sb.open(name, mode | std::ios_base::out))
00457     this->setstate(std::ios_base::failbit);
00458   else
00459     this->clear();
00460 }
00461 
00462 // Attach to file and go into fail() state if unsuccessful
00463 void
00464 gzofstream::attach(int fd,
00465                    std::ios_base::openmode mode)
00466 {
00467   if (!sb.attach(fd, mode | std::ios_base::out))
00468     this->setstate(std::ios_base::failbit);
00469   else
00470     this->clear();
00471 }
00472 
00473 // Close file
00474 void
00475 gzofstream::close()
00476 {
00477   if (!sb.close())
00478     this->setstate(std::ios_base::failbit);
00479 }

Generated on Fri Apr 14 22:57:28 2006 for minix by  doxygen 1.4.6