zfstream.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2005-2012 Ludwig Schwardt, Kevin Ruland
00004 
00005 
00006 This file is part of Octave.
00007 
00008 Octave is free software; you can redistribute it and/or modify it
00009 under the terms of the GNU General Public License as published by the
00010 Free Software Foundation; either version 3 of the License, or (at your
00011 option) any later version.
00012 
00013 Octave is distributed in the hope that it will be useful, but WITHOUT
00014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00015 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00016 for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with Octave; see the file COPYING.  If not, see
00020 <http://www.gnu.org/licenses/>.
00021 
00022 */
00023 
00024 /*
00025 
00026  This file is adapted from the zlib 1.2.2 contrib/iostream3 code,
00027  written by
00028 
00029    Ludwig Schwardt <schwardt@sun.ac.za>
00030    original version by Kevin Ruland <kevin@rodin.wustl.edu>
00031 
00032 */
00033 
00034 #ifdef HAVE_CONFIG_H
00035 #include <config.h>
00036 #endif
00037 
00038 #include <iostream>
00039 
00040 #include "zfstream.h"
00041 
00042 #ifdef HAVE_ZLIB
00043 
00044 #include <cstring>          // for strcpy, strcat, strlen (mode strings)
00045 #include <cstdio>           // for BUFSIZ
00046 
00047 // Internal buffer sizes (default and "unbuffered" versions)
00048 #define STASHED_CHARACTERS 16
00049 #define BIGBUFSIZE (256 * 1024 + STASHED_CHARACTERS)
00050 #define SMALLBUFSIZE 1
00051 
00052 /*****************************************************************************/
00053 
00054 // Default constructor
00055 gzfilebuf::gzfilebuf()
00056 : file(0), io_mode(std::ios_base::openmode(0)), own_fd(false),
00057   buffer(0), buffer_size(BIGBUFSIZE), own_buffer(true)
00058 {
00059   // No buffers to start with
00060   this->disable_buffer();
00061 }
00062 
00063 // Destructor
00064 gzfilebuf::~gzfilebuf()
00065 {
00066   // Sync output buffer and close only if responsible for file
00067   // (i.e. attached streams should be left open at this stage)
00068   this->sync();
00069   if (own_fd)
00070     this->close();
00071   // Make sure internal buffer is deallocated
00072   this->disable_buffer();
00073 }
00074 
00075 // Set compression level and strategy
00076 int
00077 gzfilebuf::setcompression(int comp_level,
00078                           int comp_strategy)
00079 {
00080   return gzsetparams(file, comp_level, comp_strategy);
00081 }
00082 
00083 // Open gzipped file
00084 gzfilebuf*
00085 gzfilebuf::open(const char *name,
00086                 std::ios_base::openmode mode)
00087 {
00088   // Fail if file already open
00089   if (this->is_open())
00090     return 0;
00091   // Don't support simultaneous read/write access (yet)
00092   if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
00093     return 0;
00094 
00095   // Build mode string for gzopen and check it [27.8.1.3.2]
00096   char char_mode[6] = "\0\0\0\0\0";
00097   if (!this->open_mode(mode, char_mode))
00098     return 0;
00099 
00100   // Attempt to open file
00101   if ((file = gzopen(name, char_mode)) == 0)
00102     return 0;
00103 
00104   // On success, allocate internal buffer and set flags
00105   this->enable_buffer();
00106   io_mode = mode;
00107   own_fd = true;
00108   return this;
00109 }
00110 
00111 // Attach to gzipped file
00112 gzfilebuf*
00113 gzfilebuf::attach(int fd,
00114                   std::ios_base::openmode mode)
00115 {
00116   // Fail if file already open
00117   if (this->is_open())
00118     return 0;
00119   // Don't support simultaneous read/write access (yet)
00120   if ((mode & std::ios_base::in) && (mode & std::ios_base::out))
00121     return 0;
00122 
00123   // Build mode string for gzdopen and check it [27.8.1.3.2]
00124   char char_mode[6] = "\0\0\0\0\0";
00125   if (!this->open_mode(mode, char_mode))
00126     return 0;
00127 
00128   // Attempt to attach to file
00129   if ((file = gzdopen(fd, char_mode)) == 0)
00130     return 0;
00131 
00132   // On success, allocate internal buffer and set flags
00133   this->enable_buffer();
00134   io_mode = mode;
00135   own_fd = false;
00136   return this;
00137 }
00138 
00139 // Close gzipped file
00140 gzfilebuf*
00141 gzfilebuf::close()
00142 {
00143   // Fail immediately if no file is open
00144   if (!this->is_open())
00145     return 0;
00146   // Assume success
00147   gzfilebuf* retval = this;
00148   // Attempt to sync and close gzipped file
00149   if (this->sync() == -1)
00150     retval = 0;
00151   if (gzclose(file) < 0)
00152     retval = 0;
00153   // File is now gone anyway (postcondition [27.8.1.3.8])
00154   file = 0;
00155   own_fd = false;
00156   // Destroy internal buffer if it exists
00157   this->disable_buffer();
00158   return retval;
00159 }
00160 
00161 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00162 
00163 // Convert int open mode to mode string
00164 bool
00165 gzfilebuf::open_mode(std::ios_base::openmode mode,
00166                      char* c_mode) const
00167 {
00168   // FIXME -- do we need testb?
00169   // bool testb = mode & std::ios_base::binary;
00170   bool testi = mode & std::ios_base::in;
00171   bool testo = mode & std::ios_base::out;
00172   bool testt = mode & std::ios_base::trunc;
00173   bool testa = mode & std::ios_base::app;
00174 
00175   // Check for valid flag combinations - see [27.8.1.3.2] (Table 92)
00176   // Original zfstream hardcoded the compression level to maximum here...
00177   // Double the time for less than 1% size improvement seems
00178   // excessive though - keeping it at the default level
00179   // To change back, just append "9" to the next three mode strings
00180   if (!testi && testo && !testt && !testa)
00181     strcpy(c_mode, "w");
00182   if (!testi && testo && !testt && testa)
00183     strcpy(c_mode, "a");
00184   if (!testi && testo && testt && !testa)
00185     strcpy(c_mode, "w");
00186   if (testi && !testo && !testt && !testa)
00187     strcpy(c_mode, "r");
00188   // No read/write mode yet
00189 //  if (testi && testo && !testt && !testa)
00190 //    strcpy(c_mode, "r+");
00191 //  if (testi && testo && testt && !testa)
00192 //    strcpy(c_mode, "w+");
00193 
00194   // Mode string should be empty for invalid combination of flags
00195   if (strlen(c_mode) == 0)
00196     return false;
00197 
00198   strcat(c_mode, "b");
00199 
00200   return true;
00201 }
00202 
00203 // Determine number of characters in internal get buffer
00204 std::streamsize
00205 gzfilebuf::showmanyc()
00206 {
00207   // Calls to underflow will fail if file not opened for reading
00208   if (!this->is_open() || !(io_mode & std::ios_base::in))
00209     return -1;
00210   // Make sure get area is in use
00211   if (this->gptr() && (this->gptr() < this->egptr()))
00212     return std::streamsize(this->egptr() - this->gptr());
00213   else
00214     return 0;
00215 }
00216 
00217 // Puts back a character to the stream in two cases. Firstly, when there
00218 // is no putback position available, and secondly when the character putback
00219 // differs from the one in the file. We can only support the first case
00220 // with gzipped files.
00221 gzfilebuf::int_type
00222 gzfilebuf::pbackfail (gzfilebuf::int_type c)
00223 {
00224   if (this->is_open())
00225     {
00226       if (gzseek (file, this->gptr() - this->egptr() - 1, SEEK_CUR) < 0)
00227         return traits_type::eof();
00228 
00229       // Invalidates contents of the buffer
00230       enable_buffer ();
00231 
00232       // Attempt to fill internal buffer from gzipped file
00233       // (buffer must be guaranteed to exist...)
00234       int bytes_read = gzread(file, buffer, buffer_size);
00235       // Indicates error or EOF
00236       if (bytes_read <= 0)
00237         {
00238           // Reset get area
00239           this->setg(buffer, buffer, buffer);
00240           return traits_type::eof();
00241         }
00242 
00243       // Make all bytes read from file available as get area
00244       this->setg(buffer, buffer, buffer + bytes_read);
00245 
00246       // If next character in get area differs from putback character
00247       // flag a failure
00248       gzfilebuf::int_type ret = traits_type::to_int_type(*(this->gptr()));
00249       if (ret != c)
00250         return traits_type::eof();
00251       else
00252         return ret;
00253     }
00254   else
00255     return traits_type::eof();
00256 }
00257 
00258 // Fill get area from gzipped file
00259 gzfilebuf::int_type
00260 gzfilebuf::underflow()
00261 {
00262   // If something is left in the get area by chance, return it
00263   // (this shouldn't normally happen, as underflow is only supposed
00264   // to be called when gptr >= egptr, but it serves as error check)
00265   if (this->gptr() && (this->gptr() < this->egptr()))
00266     return traits_type::to_int_type(*(this->gptr()));
00267 
00268   // If the file hasn't been opened for reading, produce error
00269   if (!this->is_open() || !(io_mode & std::ios_base::in))
00270     return traits_type::eof();
00271 
00272   // Copy the final characters to the front of the buffer
00273   int stash = 0;
00274   if (this->eback() && buffer && buffer_size > STASHED_CHARACTERS)
00275     {
00276       char_type *ptr1 = buffer;
00277       char_type *ptr2 = this->egptr() - STASHED_CHARACTERS + 1;
00278       if (ptr2 > this->eback())
00279         while (stash++ <= STASHED_CHARACTERS)
00280           *ptr1++ = *ptr2++;
00281     }
00282 
00283   // Attempt to fill internal buffer from gzipped file
00284   // (buffer must be guaranteed to exist...)
00285   int bytes_read = gzread(file, buffer + stash, buffer_size - stash);
00286 
00287   // Indicates error or EOF
00288   if (bytes_read <= 0)
00289   {
00290     // Reset get area
00291     this->setg(buffer, buffer, buffer);
00292     return traits_type::eof();
00293   }
00294   // Make all bytes read from file plus the stash available as get area
00295   this->setg(buffer, buffer + stash, buffer + bytes_read + stash);
00296 
00297   // Return next character in get area
00298   return traits_type::to_int_type(*(this->gptr()));
00299 }
00300 
00301 // Write put area to gzipped file
00302 gzfilebuf::int_type
00303 gzfilebuf::overflow(int_type c)
00304 {
00305   // Determine whether put area is in use
00306   if (this->pbase())
00307   {
00308     // Double-check pointer range
00309     if (this->pptr() > this->epptr() || this->pptr() < this->pbase())
00310       return traits_type::eof();
00311     // Add extra character to buffer if not EOF
00312     if (!traits_type::eq_int_type(c, traits_type::eof()))
00313     {
00314       *(this->pptr()) = traits_type::to_char_type(c);
00315       this->pbump(1);
00316     }
00317     // Number of characters to write to file
00318     int bytes_to_write = this->pptr() - this->pbase();
00319     // Overflow doesn't fail if nothing is to be written
00320     if (bytes_to_write > 0)
00321     {
00322       // If the file hasn't been opened for writing, produce error
00323       if (!this->is_open() || !(io_mode & std::ios_base::out))
00324         return traits_type::eof();
00325       // If gzipped file won't accept all bytes written to it, fail
00326       if (gzwrite(file, this->pbase(), bytes_to_write) != bytes_to_write)
00327         return traits_type::eof();
00328       // Reset next pointer to point to pbase on success
00329       this->pbump(-bytes_to_write);
00330     }
00331   }
00332   // Write extra character to file if not EOF
00333   else if (!traits_type::eq_int_type(c, traits_type::eof()))
00334   {
00335     // If the file hasn't been opened for writing, produce error
00336     if (!this->is_open() || !(io_mode & std::ios_base::out))
00337       return traits_type::eof();
00338     // Impromptu char buffer (allows "unbuffered" output)
00339     char_type last_char = traits_type::to_char_type(c);
00340     // If gzipped file won't accept this character, fail
00341     if (gzwrite(file, &last_char, 1) != 1)
00342       return traits_type::eof();
00343   }
00344 
00345   // If you got here, you have succeeded (even if c was EOF)
00346   // The return value should therefore be non-EOF
00347   if (traits_type::eq_int_type(c, traits_type::eof()))
00348     return traits_type::not_eof(c);
00349   else
00350     return c;
00351 }
00352 
00353 // Assign new buffer
00354 std::streambuf*
00355 gzfilebuf::setbuf(char_type* p,
00356                   std::streamsize n)
00357 {
00358   // First make sure stuff is sync'ed, for safety
00359   if (this->sync() == -1)
00360     return 0;
00361   // If buffering is turned off on purpose via setbuf(0,0), still allocate one...
00362   // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at
00363   // least a buffer of size 1 (very inefficient though, therefore make it bigger?)
00364   // This follows from [27.5.2.4.3]/12 (gptr needs to point at something, it seems)
00365   if (!p || !n)
00366   {
00367     // Replace existing buffer (if any) with small internal buffer
00368     this->disable_buffer();
00369     buffer = 0;
00370     buffer_size = 0;
00371     own_buffer = true;
00372     this->enable_buffer();
00373   }
00374   else
00375   {
00376     // Replace existing buffer (if any) with external buffer
00377     this->disable_buffer();
00378     buffer = p;
00379     buffer_size = n;
00380     own_buffer = false;
00381     this->enable_buffer();
00382   }
00383   return this;
00384 }
00385 
00386 // Write put area to gzipped file (i.e. ensures that put area is empty)
00387 int
00388 gzfilebuf::sync()
00389 {
00390   return traits_type::eq_int_type(this->overflow(), traits_type::eof()) ? -1 : 0;
00391 }
00392 
00393 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00394 
00395 // Allocate internal buffer
00396 void
00397 gzfilebuf::enable_buffer()
00398 {
00399   // If internal buffer required, allocate one
00400   if (own_buffer && !buffer)
00401   {
00402     // Check for buffered vs. "unbuffered"
00403     if (buffer_size > 0)
00404     {
00405       // Allocate internal buffer
00406       buffer = new char_type[buffer_size];
00407       // Get area starts empty and will be expanded by underflow as need arises
00408       this->setg(buffer, buffer, buffer);
00409       // Setup entire internal buffer as put area.
00410       // The one-past-end pointer actually points to the last element of the buffer,
00411       // so that overflow(c) can safely add the extra character c to the sequence.
00412       // These pointers remain in place for the duration of the buffer
00413       this->setp(buffer, buffer + buffer_size - 1);
00414     }
00415     else
00416     {
00417       // Even in "unbuffered" case, (small?) get buffer is still required
00418       buffer_size = SMALLBUFSIZE;
00419       buffer = new char_type[buffer_size];
00420       this->setg(buffer, buffer, buffer);
00421       // "Unbuffered" means no put buffer
00422       this->setp(0, 0);
00423     }
00424   }
00425   else
00426   {
00427     // If buffer already allocated, reset buffer pointers just to make sure no
00428     // stale chars are lying around
00429     this->setg(buffer, buffer, buffer);
00430     this->setp(buffer, buffer + buffer_size - 1);
00431   }
00432 }
00433 
00434 // Destroy internal buffer
00435 void
00436 gzfilebuf::disable_buffer()
00437 {
00438   // If internal buffer exists, deallocate it
00439   if (own_buffer && buffer)
00440   {
00441     // Preserve unbuffered status by zeroing size
00442     if (!this->pbase())
00443       buffer_size = 0;
00444     delete[] buffer;
00445     buffer = 0;
00446     this->setg(0, 0, 0);
00447     this->setp(0, 0);
00448   }
00449   else
00450   {
00451     // Reset buffer pointers to initial state if external buffer exists
00452     this->setg(buffer, buffer, buffer);
00453     if (buffer)
00454       this->setp(buffer, buffer + buffer_size - 1);
00455     else
00456       this->setp(0, 0);
00457   }
00458 }
00459 
00460 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
00461 
00462 // Seek functions
00463 gzfilebuf::pos_type
00464 gzfilebuf::seekoff(off_type off, std::ios_base::seekdir way,
00465                    std::ios_base::openmode)
00466 {
00467   pos_type ret = pos_type (off_type (-1));
00468 
00469   if (this->is_open())
00470     {
00471       off_type computed_off = off;
00472 
00473       if ((io_mode & std::ios_base::in) && way == std::ios_base::cur)
00474         computed_off += this->gptr() - this->egptr();
00475 
00476       // Handle tellg/tellp as a special case up front, no need to seek
00477       // or invalidate get/put buffers
00478       if (off == 0 && way == std::ios_base::cur)
00479         return pos_type (gztell (file) + computed_off);
00480 
00481       if (way == std::ios_base::beg)
00482         ret = pos_type (gzseek (file, computed_off, SEEK_SET));
00483       else if (way == std::ios_base::cur)
00484         ret = pos_type (gzseek (file, computed_off, SEEK_CUR));
00485       else
00486         // Can't seek from end of a gzipped file, so this will give -1
00487         ret = pos_type (gzseek (file, computed_off, SEEK_END));
00488 
00489       if (io_mode & std::ios_base::in)
00490         // Invalidates contents of the buffer
00491         enable_buffer ();
00492       else
00493         // flush contents of buffer to file
00494         overflow ();
00495     }
00496 
00497   return ret;
00498 }
00499 
00500 gzfilebuf::pos_type
00501 gzfilebuf::seekpos(pos_type sp, std::ios_base::openmode)
00502 {
00503   pos_type ret = pos_type (off_type (-1));
00504 
00505   if (this->is_open ())
00506     {
00507       ret = pos_type (gzseek (file, sp, SEEK_SET));
00508 
00509       if (io_mode & std::ios_base::in)
00510         // Invalidates contents of the buffer
00511         enable_buffer ();
00512       else
00513         // flush contents of buffer to file
00514         overflow ();
00515     }
00516 
00517   return ret;
00518 }
00519 
00520 /*****************************************************************************/
00521 
00522 // Default constructor initializes stream buffer
00523 gzifstream::gzifstream()
00524 : std::istream(0), sb()
00525 { this->init(&sb); }
00526 
00527 // Initialize stream buffer and open file
00528 gzifstream::gzifstream(const char* name,
00529                        std::ios_base::openmode mode)
00530 : std::istream(0), sb()
00531 {
00532   this->init(&sb);
00533   this->open(name, mode);
00534 }
00535 
00536 // Initialize stream buffer and attach to file
00537 gzifstream::gzifstream(int fd,
00538                        std::ios_base::openmode mode)
00539 : std::istream(0), sb()
00540 {
00541   this->init(&sb);
00542   this->attach(fd, mode);
00543 }
00544 
00545 // Open file and go into fail() state if unsuccessful
00546 void
00547 gzifstream::open(const char* name,
00548                  std::ios_base::openmode mode)
00549 {
00550   if (!sb.open(name, mode | std::ios_base::in))
00551     this->setstate(std::ios_base::failbit);
00552   else
00553     this->clear();
00554 }
00555 
00556 // Attach to file and go into fail() state if unsuccessful
00557 void
00558 gzifstream::attach(int fd,
00559                    std::ios_base::openmode mode)
00560 {
00561   if (!sb.attach(fd, mode | std::ios_base::in))
00562     this->setstate(std::ios_base::failbit);
00563   else
00564     this->clear();
00565 }
00566 
00567 // Close file
00568 void
00569 gzifstream::close()
00570 {
00571   if (!sb.close())
00572     this->setstate(std::ios_base::failbit);
00573 }
00574 
00575 /*****************************************************************************/
00576 
00577 // Default constructor initializes stream buffer
00578 gzofstream::gzofstream()
00579 : std::ostream(0), sb()
00580 { this->init(&sb); }
00581 
00582 // Initialize stream buffer and open file
00583 gzofstream::gzofstream(const char* name,
00584                        std::ios_base::openmode mode)
00585 : std::ostream(0), sb()
00586 {
00587   this->init(&sb);
00588   this->open(name, mode);
00589 }
00590 
00591 // Initialize stream buffer and attach to file
00592 gzofstream::gzofstream(int fd,
00593                        std::ios_base::openmode mode)
00594 : std::ostream(0), sb()
00595 {
00596   this->init(&sb);
00597   this->attach(fd, mode);
00598 }
00599 
00600 // Open file and go into fail() state if unsuccessful
00601 void
00602 gzofstream::open(const char* name,
00603                  std::ios_base::openmode mode)
00604 {
00605   if (!sb.open(name, mode | std::ios_base::out))
00606     this->setstate(std::ios_base::failbit);
00607   else
00608     this->clear();
00609 }
00610 
00611 // Attach to file and go into fail() state if unsuccessful
00612 void
00613 gzofstream::attach(int fd,
00614                    std::ios_base::openmode mode)
00615 {
00616   if (!sb.attach(fd, mode | std::ios_base::out))
00617     this->setstate(std::ios_base::failbit);
00618   else
00619     this->clear();
00620 }
00621 
00622 // Close file
00623 void
00624 gzofstream::close()
00625 {
00626   if (!sb.close())
00627     this->setstate(std::ios_base::failbit);
00628 }
00629 
00630 #endif // HAVE_ZLIB
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines