# HG changeset patch # User Rik # Date 1630896002 25200 # Node ID d809b99f128080cf6411c78922e99ab7bbe25675 # Parent a7813409b8c6ad4397adf7ce9482b47eec3d56ad maint: rename "zfstream.[h|cc]" to "gzfstream.[h|cc]" to match class name. * gzfstream.cc: Renamed from zfstream.cc * gzfstream.h: Renamed from zfstream.h * libinterp/corefcn/module.mk: Rename files in build system. * load-save.cc: Update #include to renamed file. diff -r a7813409b8c6 -r d809b99f1280 libinterp/corefcn/gzfstream.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/gzfstream.cc Sun Sep 05 19:40:02 2021 -0700 @@ -0,0 +1,621 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2005-2021 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or . +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// . +// +//////////////////////////////////////////////////////////////////////// + +/* + + This file is adapted from the zlib 1.2.2 contrib/iostream3 code, + written by + + Ludwig Schwardt + original version by Kevin Ruland + +*/ + +#if defined (HAVE_CONFIG_H) +# include "config.h" +#endif + +#include +#include +#include + +#include "gzfstream.h" + +#if defined (HAVE_ZLIB) + +// For strcpy, strcat, strlen (mode strings). +#include +// For BUFSIZ. +#include + +// Internal buffer sizes (default and "unbuffered" versions) +#define STASHED_CHARACTERS 16 +#define BIGBUFSIZE (256 * 1024 + STASHED_CHARACTERS) +#define SMALLBUFSIZE 1 + +// Default constructor +gzfilebuf::gzfilebuf () + : m_file(nullptr), m_io_mode(std::ios_base::openmode(0)), m_own_fd(false), + m_buffer(nullptr), m_buffer_size(BIGBUFSIZE), m_own_buffer(true) +{ + // No buffers to start with + this->disable_buffer (); +} + +// Destructor +gzfilebuf::~gzfilebuf () +{ + // Sync output buffer and close only if responsible for file + // (i.e., attached streams should be left open at this stage) + this->sync (); + if (m_own_fd) + this->close (); + // Make sure internal buffer is deallocated + this->disable_buffer (); +} + +// Set compression level and strategy +int +gzfilebuf::setcompression (int comp_level, int comp_strategy) +{ + return gzsetparams (m_file, comp_level, comp_strategy); +} + +// Open gzipped file +gzfilebuf* +gzfilebuf::open (const char *name, std::ios_base::openmode mode) +{ + // Fail if file already open + if (this->is_open ()) + return nullptr; + // Don't support simultaneous read/write access (yet) + if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) + return nullptr; + + // Build mode string for gzopen and check it [27.8.1.3.2] + char char_mode[6] = "\0\0\0\0\0"; + if (! this->open_mode (mode, char_mode)) + return nullptr; + + // Attempt to open file + if ((m_file = gzopen (name, char_mode)) == nullptr) + return nullptr; + + // On success, allocate internal buffer and set flags + this->enable_buffer (); + m_io_mode = mode; + m_own_fd = true; + return this; +} + +// Attach to gzipped file +gzfilebuf* +gzfilebuf::attach (int fd, std::ios_base::openmode mode) +{ + // Fail if file already open + if (this->is_open ()) + return nullptr; + // Don't support simultaneous read/write access (yet) + if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) + return nullptr; + + // Build mode string for gzdopen and check it [27.8.1.3.2] + char char_mode[6] = "\0\0\0\0\0"; + if (! this->open_mode (mode, char_mode)) + return nullptr; + + // Attempt to attach to file + if ((m_file = gzdopen (fd, char_mode)) == nullptr) + return nullptr; + + // On success, allocate internal buffer and set flags + this->enable_buffer (); + m_io_mode = mode; + m_own_fd = false; + return this; +} + +// Close gzipped file +gzfilebuf* +gzfilebuf::close () +{ + // Fail immediately if no file is open + if (! this->is_open ()) + return nullptr; + // Assume success + gzfilebuf *retval = this; + // Attempt to sync and close gzipped file + if (this->sync () == -1) + retval = nullptr; + if (gzclose (m_file) < 0) + retval = nullptr; + // File is now gone anyway (postcondition [27.8.1.3.8]) + m_file = nullptr; + m_own_fd = false; + // Destroy internal buffer if it exists + this->disable_buffer (); + return retval; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Convert int open mode to mode string +bool +gzfilebuf::open_mode (std::ios_base::openmode mode, char *c_mode) const +{ + // FIXME: do we need testb? + // bool testb = mode & std::ios_base::binary; + bool testi = mode & std::ios_base::in; + bool testo = mode & std::ios_base::out; + bool testt = mode & std::ios_base::trunc; + bool testa = mode & std::ios_base::app; + + // Check for valid flag combinations - see [27.8.1.3.2] (Table 92) + // Original zfstream hardcoded the compression level to maximum here... + // Double the time for less than 1% size improvement seems + // excessive though - keeping it at the default level + // To change back, just append "9" to the next three mode strings + if (! testi && testo && ! testt && ! testa) + strcpy (c_mode, "w"); + if (! testi && testo && ! testt && testa) + strcpy (c_mode, "a"); + if (! testi && testo && testt && ! testa) + strcpy (c_mode, "w"); + if (testi && ! testo && ! testt && ! testa) + strcpy (c_mode, "r"); + // No read/write mode yet + // if (testi && testo && ! testt && ! testa) + // strcpy(c_mode, "r+"); + // if (testi && testo && testt && ! testa) + // strcpy(c_mode, "w+"); + + // Mode string should be empty for invalid combination of flags + if (strlen (c_mode) == 0) + return false; + + strcat (c_mode, "b"); + + return true; +} + +// Determine number of characters in internal get buffer +std::streamsize +gzfilebuf::showmanyc () +{ + // Calls to underflow will fail if file not opened for reading + if (! this->is_open () || ! (m_io_mode & std::ios_base::in)) + return -1; + // Make sure get area is in use + if (this->gptr () && (this->gptr () < this->egptr ())) + return std::streamsize (this->egptr () - this->gptr ()); + else + return 0; +} + +// Puts back a character to the stream in two cases. Firstly, when there +// is no putback position available, and secondly when the character putback +// differs from the one in the file. We can only support the first case +// with gzipped files. +gzfilebuf::int_type +gzfilebuf::pbackfail (gzfilebuf::int_type c) +{ + if (this->is_open ()) + { + if (gzseek (m_file, this->gptr () - this->egptr () - 1, SEEK_CUR) < 0) + return traits_type::eof (); + + // Invalidates contents of the buffer + enable_buffer (); + + // Attempt to fill internal buffer from gzipped file + // (buffer must be guaranteed to exist...) + int bytes_read = gzread (m_file, m_buffer, m_buffer_size); + // Indicates error or EOF + if (bytes_read <= 0) + { + // Reset get area + this->setg (m_buffer, m_buffer, m_buffer); + return traits_type::eof (); + } + + // Make all bytes read from file available as get area + this->setg (m_buffer, m_buffer, m_buffer + bytes_read); + + // If next character in get area differs from putback character + // flag a failure + gzfilebuf::int_type ret = traits_type::to_int_type (*(this->gptr ())); + if (ret != c) + return traits_type::eof (); + else + return ret; + } + else + return traits_type::eof (); +} + +// Fill get area from gzipped file +gzfilebuf::int_type +gzfilebuf::underflow () +{ + // If something is left in the get area by chance, return it + // (this shouldn't normally happen, as underflow is only supposed + // to be called when gptr >= egptr, but it serves as error check) + if (this->gptr () && (this->gptr () < this->egptr ())) + return traits_type::to_int_type (*(this->gptr ())); + + // If the file hasn't been opened for reading, produce error + if (! this->is_open () || ! (m_io_mode & std::ios_base::in)) + return traits_type::eof (); + + // Copy the final characters to the front of the buffer + int stash = 0; + if (this->eback () && m_buffer && m_buffer_size > STASHED_CHARACTERS) + { + char_type *ptr1 = m_buffer; + char_type *ptr2 = this->egptr () - STASHED_CHARACTERS + 1; + if (ptr2 > this->eback ()) + while (stash++ <= STASHED_CHARACTERS) + *ptr1++ = *ptr2++; + } + + // Attempt to fill internal buffer from gzipped file + // (buffer must be guaranteed to exist...) + int bytes_read = gzread (m_file, m_buffer + stash, m_buffer_size - stash); + + // Indicates error or EOF + if (bytes_read <= 0) + { + // Reset get area + this->setg (m_buffer, m_buffer, m_buffer); + return traits_type::eof (); + } + // Make all bytes read from file plus the stash available as get area + this->setg (m_buffer, m_buffer + stash, m_buffer + bytes_read + stash); + + // Return next character in get area + return traits_type::to_int_type (*(this->gptr ())); +} + +// Write put area to gzipped file +gzfilebuf::int_type +gzfilebuf::overflow (int_type c) +{ + // Determine whether put area is in use + if (this->pbase ()) + { + // Double-check pointer range + if (this->pptr () > this->epptr () || this->pptr () < this->pbase ()) + return traits_type::eof (); + // Add extra character to buffer if not EOF + if (! traits_type::eq_int_type (c, traits_type::eof ())) + { + *(this->pptr ()) = traits_type::to_char_type (c); + this->pbump (1); + } + // Number of characters to write to file + int bytes_to_write = this->pptr () - this->pbase (); + // Overflow doesn't fail if nothing is to be written + if (bytes_to_write > 0) + { + // If the file hasn't been opened for writing, produce error + if (! this->is_open () || ! (m_io_mode & std::ios_base::out)) + return traits_type::eof (); + // If gzipped file won't accept all bytes written to it, fail + if (gzwrite (m_file, this->pbase (), bytes_to_write) + != bytes_to_write) + return traits_type::eof (); + // Reset next pointer to point to pbase on success + this->pbump (-bytes_to_write); + } + } + // Write extra character to file if not EOF + else if (! traits_type::eq_int_type (c, traits_type::eof ())) + { + // If the file hasn't been opened for writing, produce error + if (! this->is_open () || ! (m_io_mode & std::ios_base::out)) + return traits_type::eof (); + // Impromptu char buffer (allows "unbuffered" output) + char_type last_char = traits_type::to_char_type (c); + // If gzipped file won't accept this character, fail + if (gzwrite (m_file, &last_char, 1) != 1) + return traits_type::eof (); + } + + // If you got here, you have succeeded (even if c was EOF) + // The return value should therefore be non-EOF + if (traits_type::eq_int_type (c, traits_type::eof ())) + return traits_type::not_eof (c); + else + return c; +} + +// Assign new buffer +std::streambuf* +gzfilebuf::setbuf (char_type *p, std::streamsize n) +{ + // First make sure stuff is sync'ed, for safety + if (this->sync () == -1) + return nullptr; + // If buffering is turned off on purpose via setbuf(0,0), still allocate one. + // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at + // least a buffer of size 1 (very inefficient though, therefore make it + // bigger?). This follows from [27.5.2.4.3]/12 (gptr needs to point at + // something, it seems). + if (! p || ! n) + { + // Replace existing buffer (if any) with small internal buffer + this->disable_buffer (); + m_buffer = nullptr; + m_buffer_size = 0; + m_own_buffer = true; + this->enable_buffer (); + } + else + { + // Replace existing buffer (if any) with external buffer + this->disable_buffer (); + m_buffer = p; + m_buffer_size = n; + m_own_buffer = false; + this->enable_buffer (); + } + return this; +} + +// Write put area to gzipped file (i.e., ensures that put area is empty) +int +gzfilebuf::sync () +{ + return traits_type::eq_int_type (this->overflow (), + traits_type::eof ()) ? -1 : 0; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Allocate internal buffer +void +gzfilebuf::enable_buffer () +{ + // If internal buffer required, allocate one + if (m_own_buffer && ! m_buffer) + { + // Check for buffered vs. "unbuffered" + if (m_buffer_size > 0) + { + // Allocate internal buffer + m_buffer = new char_type [m_buffer_size]; + // Get area starts empty and will be expanded by underflow as needed + this->setg (m_buffer, m_buffer, m_buffer); + // Setup entire internal buffer as put area. + // The one-past-end pointer actually points to the last element of + // the buffer, so that overflow(c) can safely add the extra character + // c to the sequence. These pointers remain in place for the + // duration of the buffer + this->setp (m_buffer, m_buffer + m_buffer_size - 1); + } + else + { + // Even in "unbuffered" case, (small?) get buffer is still required + m_buffer_size = SMALLBUFSIZE; + m_buffer = new char_type [m_buffer_size]; + this->setg (m_buffer, m_buffer, m_buffer); + // "Unbuffered" means no put buffer + this->setp (nullptr, nullptr); + } + } + else + { + // If buffer already allocated, reset buffer pointers just to make sure no + // stale chars are lying around + this->setg (m_buffer, m_buffer, m_buffer); + this->setp (m_buffer, m_buffer + m_buffer_size - 1); + } +} + +// Destroy internal buffer +void +gzfilebuf::disable_buffer () +{ + // If internal buffer exists, deallocate it + if (m_own_buffer && m_buffer) + { + // Preserve unbuffered status by zeroing size + if (! this->pbase ()) + m_buffer_size = 0; + delete [] m_buffer; + m_buffer = nullptr; + this->setg (nullptr, nullptr, nullptr); + this->setp (nullptr, nullptr); + } + else + { + // Reset buffer pointers to initial state if external buffer exists + this->setg (m_buffer, m_buffer, m_buffer); + if (m_buffer) + this->setp (m_buffer, m_buffer + m_buffer_size - 1); + else + this->setp (nullptr, nullptr); + } +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +// Seek functions +gzfilebuf::pos_type +gzfilebuf::seekoff (off_type off, std::ios_base::seekdir way, + std::ios_base::openmode) +{ + pos_type ret = pos_type (off_type (-1)); + + if (this->is_open ()) + { + off_type computed_off = off; + + if ((m_io_mode & std::ios_base::in) && way == std::ios_base::cur) + computed_off += this->gptr () - this->egptr (); + + // Handle tellg/tellp as a special case up front, no need to seek + // or invalidate get/put buffers + if (off == 0 && way == std::ios_base::cur) + return pos_type (gztell (m_file) + computed_off); + + if (way == std::ios_base::beg) + ret = pos_type (gzseek (m_file, computed_off, SEEK_SET)); + else if (way == std::ios_base::cur) + ret = pos_type (gzseek (m_file, computed_off, SEEK_CUR)); + else + // Can't seek from end of a gzipped file, so this will give -1 + ret = pos_type (gzseek (m_file, computed_off, SEEK_END)); + + if (m_io_mode & std::ios_base::in) + // Invalidates contents of the buffer + enable_buffer (); + else + // flush contents of buffer to file + overflow (); + } + + return ret; +} + +gzfilebuf::pos_type +gzfilebuf::seekpos (pos_type sp, std::ios_base::openmode) +{ + pos_type ret = pos_type (off_type (-1)); + + if (this->is_open ()) + { + ret = pos_type (gzseek (m_file, sp, SEEK_SET)); + + if (m_io_mode & std::ios_base::in) + // Invalidates contents of the buffer + enable_buffer (); + else + // flush contents of buffer to file + overflow (); + } + + return ret; +} + +// Default constructor initializes stream buffer +gzifstream::gzifstream () + : std::istream (nullptr), sb () +{ this->init (&sb); } + +// Initialize stream buffer and open file +gzifstream::gzifstream (const char *name, std::ios_base::openmode mode) + : std::istream (nullptr), sb () +{ + this->init (&sb); + this->open (name, mode); +} + +// Initialize stream buffer and attach to file +gzifstream::gzifstream (int fd, std::ios_base::openmode mode) + : std::istream (nullptr), sb () +{ + this->init (&sb); + this->attach (fd, mode); +} + +// Open file and go into fail() state if unsuccessful +void +gzifstream::open (const char *name, std::ios_base::openmode mode) +{ + if (! sb.open (name, mode | std::ios_base::in)) + this->setstate (std::ios_base::failbit); + else + this->clear (); +} + +// Attach to file and go into fail() state if unsuccessful +void +gzifstream::attach (int fd, std::ios_base::openmode mode) +{ + if (! sb.attach (fd, mode | std::ios_base::in)) + this->setstate (std::ios_base::failbit); + else + this->clear (); +} + +// Close file +void +gzifstream::close () +{ + if (! sb.close ()) + this->setstate (std::ios_base::failbit); +} + +// Default constructor initializes stream buffer +gzofstream::gzofstream () + : std::ostream (nullptr), sb () +{ this->init (&sb); } + +// Initialize stream buffer and open file +gzofstream::gzofstream (const char *name, std::ios_base::openmode mode) + : std::ostream (nullptr), sb () +{ + this->init (&sb); + this->open (name, mode); +} + +// Initialize stream buffer and attach to file +gzofstream::gzofstream (int fd, std::ios_base::openmode mode) + : std::ostream (nullptr), sb () +{ + this->init (&sb); + this->attach (fd, mode); +} + +// Open file and go into fail() state if unsuccessful +void +gzofstream::open (const char *name, std::ios_base::openmode mode) +{ + if (! sb.open (name, mode | std::ios_base::out)) + this->setstate (std::ios_base::failbit); + else + this->clear (); +} + +// Attach to file and go into fail() state if unsuccessful +void +gzofstream::attach (int fd, std::ios_base::openmode mode) +{ + if (! sb.attach (fd, mode | std::ios_base::out)) + this->setstate (std::ios_base::failbit); + else + this->clear (); +} + +// Close file +void +gzofstream::close () +{ + if (! sb.close ()) + this->setstate (std::ios_base::failbit); +} + +#endif diff -r a7813409b8c6 -r d809b99f1280 libinterp/corefcn/gzfstream.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/corefcn/gzfstream.h Sun Sep 05 19:40:02 2021 -0700 @@ -0,0 +1,509 @@ +//////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2005-2021 The Octave Project Developers +// +// See the file COPYRIGHT.md in the top-level directory of this +// distribution or . +// +// This file is part of Octave. +// +// Octave is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Octave is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Octave; see the file COPYING. If not, see +// . +// +//////////////////////////////////////////////////////////////////////// + +/* + + This file is adapted from the zlib 1.2.2 contrib/iostream3 code, + written by + + Ludwig Schwardt + original version by Kevin Ruland + +*/ + +#if ! defined (octave_zfsstream_h) +#define octave_zfsstream_h 1 + +#include "octave-config.h" + +#if defined (HAVE_ZLIB) + +#include + +#include "zlib.h" + +/** + * @brief Gzipped file stream buffer class. + * + * This class implements basic_filebuf for gzipped files. It doesn't yet + * support seeking (allowed by zlib but slow/limited), putback and read/write + * access * (tricky). Otherwise, it attempts to be a drop-in replacement for + * the standard file streambuf. +*/ +class gzfilebuf : public std::streambuf +{ +public: + // Default constructor. + gzfilebuf (); + + // No copying! + + gzfilebuf (const gzfilebuf&) = delete; + + gzfilebuf& operator = (const gzfilebuf&) = delete; + + // Destructor. + virtual ~gzfilebuf (); + + /** + * @brief Set compression level and strategy on the fly. + * @param comp_level Compression level (see zlib.h for allowed values) + * @param comp_strategy Compression strategy (see zlib.h for allowed values) + * @return Z_OK on success, Z_STREAM_ERROR otherwise. + * + * Unfortunately, these parameters cannot be modified separately, as the + * previous zfstream version assumed. Since the strategy is seldom changed, + * it can default and setcompression(level) then becomes like the old + * setcompressionlevel(level). + */ + int + setcompression (int comp_level, + int comp_strategy = Z_DEFAULT_STRATEGY); + + /** + * @brief Check if file is open. + * @return True if file is open. + */ + bool + is_open () const { return (m_file != nullptr); } + + /** + * @brief Open gzipped file. + * @param name Filename. + * @param mode Open mode flags. + * @return @c this on success, NULL on failure. + */ + gzfilebuf* + open (const char *name, + std::ios_base::openmode mode); + + /** + * @brief Attach to already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags. + * @return @c this on success, NULL on failure. + */ + gzfilebuf* + attach (int fd, + std::ios_base::openmode mode); + + /** + * @brief Close gzipped file. + * @return @c this on success, NULL on failure. + */ + gzfilebuf* + close (); + +protected: + /** + * @brief Convert ios open mode int to mode string used by zlib. + * @return True if valid mode flag combination. + */ + bool + open_mode (std::ios_base::openmode mode, + char *c_mode) const; + + /** + * @brief Number of characters available in stream buffer. + * @return Number of characters. + * + * This indicates number of characters in get area of stream buffer. + * These characters can be read without accessing the gzipped file. + */ + virtual std::streamsize + showmanyc (); + + /** + * @brief Fill get area from gzipped file. + * @return First character in get area on success, EOF on error. + * + * This actually reads characters from gzipped file to stream + * buffer. Always buffered. + */ + virtual int_type + underflow (); + + /** + * @brief Write put area to gzipped file. + * @param c Extra character to add to buffer contents. + * @return Non-EOF on success, EOF on error. + * + * This actually writes characters in stream buffer to + * gzipped file. With unbuffered output this is done one + * character at a time. + */ + virtual int_type + overflow (int_type c = traits_type::eof ()); + + /** + * @brief Installs external stream buffer. + * @param p Pointer to char buffer. + * @param n Size of external buffer. + * @return @c this on success, NULL on failure. + * + * Call setbuf(0,0) to enable unbuffered output. + */ + virtual std::streambuf* + setbuf (char_type *p, + std::streamsize n); + + /** + * @brief Flush stream buffer to file. + * @return 0 on success, -1 on error. + * + * This calls underflow(EOF) to do the job. + */ + virtual int + sync (); + + /** + * @brief Alters the stream positions. + * + * Each derived class provides its own appropriate behavior. + */ + virtual pos_type + seekoff (off_type off, std::ios_base::seekdir way, + std::ios_base::openmode mode = + std::ios_base::in | std::ios_base::out); + + /** + * @brief Alters the stream positions. + * + * Each derived class provides its own appropriate behavior. + */ + virtual pos_type + seekpos (pos_type sp, std::ios_base::openmode mode = + std::ios_base::in | std::ios_base::out); + + virtual int_type + pbackfail (int_type c = traits_type::eof ()); + +// +// Some future enhancements +// +// virtual int_type uflow(); +// virtual int_type pbackfail(int_type c = traits_type::eof()); + +private: + + /** + * @brief Allocate internal buffer. + * + * This function is safe to call multiple times. It will ensure + * that a proper internal buffer exists if it is required. If the + * buffer already exists or is external, the buffer pointers will be + * reset to their original state. + */ + void + enable_buffer (); + + /** + * @brief Destroy internal buffer. + * + * This function is safe to call multiple times. It will ensure + * that the internal buffer is deallocated if it exists. In any + * case, it will also reset the buffer pointers. + */ + void + disable_buffer (); + + /** + * Underlying file pointer. + */ + gzFile m_file; + + /** + * Mode in which file was opened. + */ + std::ios_base::openmode m_io_mode; + + /** + * @brief True if this object owns file descriptor. + * + * This makes the class responsible for closing the file + * upon destruction. + */ + bool m_own_fd; + + /** + * @brief Stream buffer. + * + * For simplicity this remains allocated on the free store for the + * entire life span of the gzfilebuf object, unless replaced by setbuf. + */ + char_type *m_buffer; + + /** + * @brief Stream buffer size. + * + * Defaults to system default buffer size (typically 8192 bytes). + * Modified by setbuf. + */ + std::streamsize m_buffer_size; + + /** + * @brief True if this object owns stream buffer. + * + * This makes the class responsible for deleting the buffer + * upon destruction. + */ + bool m_own_buffer; +}; + +/** + * @brief Gzipped file input stream class. + * + * This class implements ifstream for gzipped files. Seeking and putback + * is not supported yet. +*/ +class gzifstream : public std::istream +{ +public: + // Default constructor + gzifstream (); + + /** + * @brief Construct stream on gzipped file to be opened. + * @param name Filename. + * @param mode Open mode flags (forced to contain ios::in). + */ + explicit + gzifstream (const char *name, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * @brief Construct stream on already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::in). + */ + explicit + gzifstream (int fd, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * Obtain underlying stream buffer. + */ + gzfilebuf* + rdbuf () const + { return const_cast(&sb); } + + /** + * @brief Check if file is open. + * @return True if file is open. + */ + bool + is_open () { return sb.is_open (); } + + /** + * @brief Open gzipped file. + * @param name Filename. + * @param mode Open mode flags (forced to contain ios::in). + * + * Stream will be in state good() if file opens successfully; + * otherwise in state fail(). This differs from the behavior of + * ifstream, which never sets the state to good() and therefore + * won't allow you to reuse the stream for a second file unless + * you manually clear() the state. The choice is a matter of + * convenience. + */ + void + open (const char *name, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * @brief Attach to already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::in). + * + * Stream will be in state good() if attach succeeded; otherwise + * in state fail(). + */ + void + attach (int fd, + std::ios_base::openmode mode = std::ios_base::in); + + /** + * @brief Close gzipped file. + * + * Stream will be in state fail() if close failed. + */ + void + close (); + +private: + /** + * Underlying stream buffer. + */ + gzfilebuf sb; +}; + +/** + * @brief Gzipped file output stream class. + * + * This class implements ofstream for gzipped files. Seeking and putback + * is not supported yet. +*/ +class gzofstream : public std::ostream +{ +public: + // Default constructor + gzofstream (); + + /** + * @brief Construct stream on gzipped file to be opened. + * @param name Filename. + * @param mode Open mode flags (forced to contain ios::out). + */ + explicit + gzofstream (const char *name, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * @brief Construct stream on already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::out). + */ + explicit + gzofstream (int fd, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * Obtain underlying stream buffer. + */ + gzfilebuf* + rdbuf () const + { return const_cast(&sb); } + + /** + * @brief Check if file is open. + * @return True if file is open. + */ + bool + is_open () { return sb.is_open (); } + + /** + * @brief Open gzipped file. + * @param name Filename. + * @param mode Open mode flags (forced to contain ios::out). + * + * Stream will be in state good() if file opens successfully; + * otherwise in state fail(). This differs from the behavior of + * ofstream, which never sets the state to good() and therefore + * won't allow you to reuse the stream for a second file unless + * you manually clear() the state. The choice is a matter of + * convenience. + */ + void + open (const char *name, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * @brief Attach to already open gzipped file. + * @param fd File descriptor. + * @param mode Open mode flags (forced to contain ios::out). + * + * Stream will be in state good() if attach succeeded; otherwise + * in state fail(). + */ + void + attach (int fd, + std::ios_base::openmode mode = std::ios_base::out); + + /** + * @brief Close gzipped file. + * + * Stream will be in state fail() if close failed. + */ + void + close (); + +private: + /** + * Underlying stream buffer. + */ + gzfilebuf sb; +}; + +/** + * @brief Gzipped file output stream manipulator class. + * + * This class defines a two-argument manipulator for gzofstream. It is used + * as base for the setcompression(int,int) manipulator. +*/ +template +class gzomanip2 +{ +public: + // Allows insertor to peek at internals + template + friend gzofstream& + operator<<(gzofstream&, + const gzomanip2&); + + // Constructor + gzomanip2 (gzofstream& (*f)(gzofstream&, T1, T2), + T1 v1, + T2 v2); +private: + // Underlying manipulator function + gzofstream& + (*func)(gzofstream&, T1, T2); + + // Arguments for manipulator function + T1 val1; + T2 val2; +}; + +// Manipulator function thunks through to stream buffer +inline gzofstream& +setcompression (gzofstream& gzs, int l, int s = Z_DEFAULT_STRATEGY) +{ + (gzs.rdbuf ())->setcompression (l, s); + return gzs; +} + +// Manipulator constructor stores arguments +template +inline +gzomanip2::gzomanip2 (gzofstream &(*f)(gzofstream &, T1, T2), + T1 v1, + T2 v2) + : func(f), val1(v1), val2(v2) +{ } + +// Insertor applies underlying manipulator function to stream +template +inline gzofstream& +operator<<(gzofstream& s, const gzomanip2& m) +{ return (*m.func)(s, m.val1, m.val2); } + +// Insert this onto stream to simplify setting of compression level +inline gzomanip2 +setcompression (int l, int s = Z_DEFAULT_STRATEGY) +{ return gzomanip2(&setcompression, l, s); } + +#endif + +#endif diff -r a7813409b8c6 -r d809b99f1280 libinterp/corefcn/load-save.cc --- a/libinterp/corefcn/load-save.cc Sun Sep 05 19:34:37 2021 -0700 +++ b/libinterp/corefcn/load-save.cc Sun Sep 05 19:40:02 2021 -0700 @@ -87,7 +87,7 @@ #endif #if defined (HAVE_ZLIB) -# include "zfstream.h" +# include "gzfstream.h" #endif OCTAVE_NAMESPACE_BEGIN diff -r a7813409b8c6 -r d809b99f1280 libinterp/corefcn/module.mk --- a/libinterp/corefcn/module.mk Sun Sep 05 19:34:37 2021 -0700 +++ b/libinterp/corefcn/module.mk Sun Sep 05 19:40:02 2021 -0700 @@ -98,7 +98,7 @@ %reldir%/xdiv.h \ %reldir%/xnorm.h \ %reldir%/xpow.h \ - %reldir%/zfstream.h + %reldir%/gzfstream.h NOINSTALL_COREFCN_INC = \ %reldir%/interpreter-private.h \ @@ -276,7 +276,7 @@ %reldir%/xdiv.cc \ %reldir%/xnorm.cc \ %reldir%/xpow.cc \ - %reldir%/zfstream.cc \ + %reldir%/gzfstream.cc \ $(NOINSTALL_COREFCN_INC) ## Special rules for sources which must be built before rest of compilation. diff -r a7813409b8c6 -r d809b99f1280 libinterp/corefcn/zfstream.cc --- a/libinterp/corefcn/zfstream.cc Sun Sep 05 19:34:37 2021 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,621 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2005-2021 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Octave is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Octave; see the file COPYING. If not, see -// . -// -//////////////////////////////////////////////////////////////////////// - -/* - - This file is adapted from the zlib 1.2.2 contrib/iostream3 code, - written by - - Ludwig Schwardt - original version by Kevin Ruland - -*/ - -#if defined (HAVE_CONFIG_H) -# include "config.h" -#endif - -#include -#include -#include - -#include "zfstream.h" - -#if defined (HAVE_ZLIB) - -// For strcpy, strcat, strlen (mode strings). -#include -// For BUFSIZ. -#include - -// Internal buffer sizes (default and "unbuffered" versions) -#define STASHED_CHARACTERS 16 -#define BIGBUFSIZE (256 * 1024 + STASHED_CHARACTERS) -#define SMALLBUFSIZE 1 - -// Default constructor -gzfilebuf::gzfilebuf () - : m_file(nullptr), m_io_mode(std::ios_base::openmode(0)), m_own_fd(false), - m_buffer(nullptr), m_buffer_size(BIGBUFSIZE), m_own_buffer(true) -{ - // No buffers to start with - this->disable_buffer (); -} - -// Destructor -gzfilebuf::~gzfilebuf () -{ - // Sync output buffer and close only if responsible for file - // (i.e., attached streams should be left open at this stage) - this->sync (); - if (m_own_fd) - this->close (); - // Make sure internal buffer is deallocated - this->disable_buffer (); -} - -// Set compression level and strategy -int -gzfilebuf::setcompression (int comp_level, int comp_strategy) -{ - return gzsetparams (m_file, comp_level, comp_strategy); -} - -// Open gzipped file -gzfilebuf* -gzfilebuf::open (const char *name, std::ios_base::openmode mode) -{ - // Fail if file already open - if (this->is_open ()) - return nullptr; - // Don't support simultaneous read/write access (yet) - if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) - return nullptr; - - // Build mode string for gzopen and check it [27.8.1.3.2] - char char_mode[6] = "\0\0\0\0\0"; - if (! this->open_mode (mode, char_mode)) - return nullptr; - - // Attempt to open file - if ((m_file = gzopen (name, char_mode)) == nullptr) - return nullptr; - - // On success, allocate internal buffer and set flags - this->enable_buffer (); - m_io_mode = mode; - m_own_fd = true; - return this; -} - -// Attach to gzipped file -gzfilebuf* -gzfilebuf::attach (int fd, std::ios_base::openmode mode) -{ - // Fail if file already open - if (this->is_open ()) - return nullptr; - // Don't support simultaneous read/write access (yet) - if ((mode & std::ios_base::in) && (mode & std::ios_base::out)) - return nullptr; - - // Build mode string for gzdopen and check it [27.8.1.3.2] - char char_mode[6] = "\0\0\0\0\0"; - if (! this->open_mode (mode, char_mode)) - return nullptr; - - // Attempt to attach to file - if ((m_file = gzdopen (fd, char_mode)) == nullptr) - return nullptr; - - // On success, allocate internal buffer and set flags - this->enable_buffer (); - m_io_mode = mode; - m_own_fd = false; - return this; -} - -// Close gzipped file -gzfilebuf* -gzfilebuf::close () -{ - // Fail immediately if no file is open - if (! this->is_open ()) - return nullptr; - // Assume success - gzfilebuf *retval = this; - // Attempt to sync and close gzipped file - if (this->sync () == -1) - retval = nullptr; - if (gzclose (m_file) < 0) - retval = nullptr; - // File is now gone anyway (postcondition [27.8.1.3.8]) - m_file = nullptr; - m_own_fd = false; - // Destroy internal buffer if it exists - this->disable_buffer (); - return retval; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -// Convert int open mode to mode string -bool -gzfilebuf::open_mode (std::ios_base::openmode mode, char *c_mode) const -{ - // FIXME: do we need testb? - // bool testb = mode & std::ios_base::binary; - bool testi = mode & std::ios_base::in; - bool testo = mode & std::ios_base::out; - bool testt = mode & std::ios_base::trunc; - bool testa = mode & std::ios_base::app; - - // Check for valid flag combinations - see [27.8.1.3.2] (Table 92) - // Original zfstream hardcoded the compression level to maximum here... - // Double the time for less than 1% size improvement seems - // excessive though - keeping it at the default level - // To change back, just append "9" to the next three mode strings - if (! testi && testo && ! testt && ! testa) - strcpy (c_mode, "w"); - if (! testi && testo && ! testt && testa) - strcpy (c_mode, "a"); - if (! testi && testo && testt && ! testa) - strcpy (c_mode, "w"); - if (testi && ! testo && ! testt && ! testa) - strcpy (c_mode, "r"); - // No read/write mode yet - // if (testi && testo && ! testt && ! testa) - // strcpy(c_mode, "r+"); - // if (testi && testo && testt && ! testa) - // strcpy(c_mode, "w+"); - - // Mode string should be empty for invalid combination of flags - if (strlen (c_mode) == 0) - return false; - - strcat (c_mode, "b"); - - return true; -} - -// Determine number of characters in internal get buffer -std::streamsize -gzfilebuf::showmanyc () -{ - // Calls to underflow will fail if file not opened for reading - if (! this->is_open () || ! (m_io_mode & std::ios_base::in)) - return -1; - // Make sure get area is in use - if (this->gptr () && (this->gptr () < this->egptr ())) - return std::streamsize (this->egptr () - this->gptr ()); - else - return 0; -} - -// Puts back a character to the stream in two cases. Firstly, when there -// is no putback position available, and secondly when the character putback -// differs from the one in the file. We can only support the first case -// with gzipped files. -gzfilebuf::int_type -gzfilebuf::pbackfail (gzfilebuf::int_type c) -{ - if (this->is_open ()) - { - if (gzseek (m_file, this->gptr () - this->egptr () - 1, SEEK_CUR) < 0) - return traits_type::eof (); - - // Invalidates contents of the buffer - enable_buffer (); - - // Attempt to fill internal buffer from gzipped file - // (buffer must be guaranteed to exist...) - int bytes_read = gzread (m_file, m_buffer, m_buffer_size); - // Indicates error or EOF - if (bytes_read <= 0) - { - // Reset get area - this->setg (m_buffer, m_buffer, m_buffer); - return traits_type::eof (); - } - - // Make all bytes read from file available as get area - this->setg (m_buffer, m_buffer, m_buffer + bytes_read); - - // If next character in get area differs from putback character - // flag a failure - gzfilebuf::int_type ret = traits_type::to_int_type (*(this->gptr ())); - if (ret != c) - return traits_type::eof (); - else - return ret; - } - else - return traits_type::eof (); -} - -// Fill get area from gzipped file -gzfilebuf::int_type -gzfilebuf::underflow () -{ - // If something is left in the get area by chance, return it - // (this shouldn't normally happen, as underflow is only supposed - // to be called when gptr >= egptr, but it serves as error check) - if (this->gptr () && (this->gptr () < this->egptr ())) - return traits_type::to_int_type (*(this->gptr ())); - - // If the file hasn't been opened for reading, produce error - if (! this->is_open () || ! (m_io_mode & std::ios_base::in)) - return traits_type::eof (); - - // Copy the final characters to the front of the buffer - int stash = 0; - if (this->eback () && m_buffer && m_buffer_size > STASHED_CHARACTERS) - { - char_type *ptr1 = m_buffer; - char_type *ptr2 = this->egptr () - STASHED_CHARACTERS + 1; - if (ptr2 > this->eback ()) - while (stash++ <= STASHED_CHARACTERS) - *ptr1++ = *ptr2++; - } - - // Attempt to fill internal buffer from gzipped file - // (buffer must be guaranteed to exist...) - int bytes_read = gzread (m_file, m_buffer + stash, m_buffer_size - stash); - - // Indicates error or EOF - if (bytes_read <= 0) - { - // Reset get area - this->setg (m_buffer, m_buffer, m_buffer); - return traits_type::eof (); - } - // Make all bytes read from file plus the stash available as get area - this->setg (m_buffer, m_buffer + stash, m_buffer + bytes_read + stash); - - // Return next character in get area - return traits_type::to_int_type (*(this->gptr ())); -} - -// Write put area to gzipped file -gzfilebuf::int_type -gzfilebuf::overflow (int_type c) -{ - // Determine whether put area is in use - if (this->pbase ()) - { - // Double-check pointer range - if (this->pptr () > this->epptr () || this->pptr () < this->pbase ()) - return traits_type::eof (); - // Add extra character to buffer if not EOF - if (! traits_type::eq_int_type (c, traits_type::eof ())) - { - *(this->pptr ()) = traits_type::to_char_type (c); - this->pbump (1); - } - // Number of characters to write to file - int bytes_to_write = this->pptr () - this->pbase (); - // Overflow doesn't fail if nothing is to be written - if (bytes_to_write > 0) - { - // If the file hasn't been opened for writing, produce error - if (! this->is_open () || ! (m_io_mode & std::ios_base::out)) - return traits_type::eof (); - // If gzipped file won't accept all bytes written to it, fail - if (gzwrite (m_file, this->pbase (), bytes_to_write) - != bytes_to_write) - return traits_type::eof (); - // Reset next pointer to point to pbase on success - this->pbump (-bytes_to_write); - } - } - // Write extra character to file if not EOF - else if (! traits_type::eq_int_type (c, traits_type::eof ())) - { - // If the file hasn't been opened for writing, produce error - if (! this->is_open () || ! (m_io_mode & std::ios_base::out)) - return traits_type::eof (); - // Impromptu char buffer (allows "unbuffered" output) - char_type last_char = traits_type::to_char_type (c); - // If gzipped file won't accept this character, fail - if (gzwrite (m_file, &last_char, 1) != 1) - return traits_type::eof (); - } - - // If you got here, you have succeeded (even if c was EOF) - // The return value should therefore be non-EOF - if (traits_type::eq_int_type (c, traits_type::eof ())) - return traits_type::not_eof (c); - else - return c; -} - -// Assign new buffer -std::streambuf* -gzfilebuf::setbuf (char_type *p, std::streamsize n) -{ - // First make sure stuff is sync'ed, for safety - if (this->sync () == -1) - return nullptr; - // If buffering is turned off on purpose via setbuf(0,0), still allocate one. - // "Unbuffered" only really refers to put [27.8.1.4.10], while get needs at - // least a buffer of size 1 (very inefficient though, therefore make it - // bigger?). This follows from [27.5.2.4.3]/12 (gptr needs to point at - // something, it seems). - if (! p || ! n) - { - // Replace existing buffer (if any) with small internal buffer - this->disable_buffer (); - m_buffer = nullptr; - m_buffer_size = 0; - m_own_buffer = true; - this->enable_buffer (); - } - else - { - // Replace existing buffer (if any) with external buffer - this->disable_buffer (); - m_buffer = p; - m_buffer_size = n; - m_own_buffer = false; - this->enable_buffer (); - } - return this; -} - -// Write put area to gzipped file (i.e., ensures that put area is empty) -int -gzfilebuf::sync () -{ - return traits_type::eq_int_type (this->overflow (), - traits_type::eof ()) ? -1 : 0; -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -// Allocate internal buffer -void -gzfilebuf::enable_buffer () -{ - // If internal buffer required, allocate one - if (m_own_buffer && ! m_buffer) - { - // Check for buffered vs. "unbuffered" - if (m_buffer_size > 0) - { - // Allocate internal buffer - m_buffer = new char_type [m_buffer_size]; - // Get area starts empty and will be expanded by underflow as needed - this->setg (m_buffer, m_buffer, m_buffer); - // Setup entire internal buffer as put area. - // The one-past-end pointer actually points to the last element of - // the buffer, so that overflow(c) can safely add the extra character - // c to the sequence. These pointers remain in place for the - // duration of the buffer - this->setp (m_buffer, m_buffer + m_buffer_size - 1); - } - else - { - // Even in "unbuffered" case, (small?) get buffer is still required - m_buffer_size = SMALLBUFSIZE; - m_buffer = new char_type [m_buffer_size]; - this->setg (m_buffer, m_buffer, m_buffer); - // "Unbuffered" means no put buffer - this->setp (nullptr, nullptr); - } - } - else - { - // If buffer already allocated, reset buffer pointers just to make sure no - // stale chars are lying around - this->setg (m_buffer, m_buffer, m_buffer); - this->setp (m_buffer, m_buffer + m_buffer_size - 1); - } -} - -// Destroy internal buffer -void -gzfilebuf::disable_buffer () -{ - // If internal buffer exists, deallocate it - if (m_own_buffer && m_buffer) - { - // Preserve unbuffered status by zeroing size - if (! this->pbase ()) - m_buffer_size = 0; - delete [] m_buffer; - m_buffer = nullptr; - this->setg (nullptr, nullptr, nullptr); - this->setp (nullptr, nullptr); - } - else - { - // Reset buffer pointers to initial state if external buffer exists - this->setg (m_buffer, m_buffer, m_buffer); - if (m_buffer) - this->setp (m_buffer, m_buffer + m_buffer_size - 1); - else - this->setp (nullptr, nullptr); - } -} - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -// Seek functions -gzfilebuf::pos_type -gzfilebuf::seekoff (off_type off, std::ios_base::seekdir way, - std::ios_base::openmode) -{ - pos_type ret = pos_type (off_type (-1)); - - if (this->is_open ()) - { - off_type computed_off = off; - - if ((m_io_mode & std::ios_base::in) && way == std::ios_base::cur) - computed_off += this->gptr () - this->egptr (); - - // Handle tellg/tellp as a special case up front, no need to seek - // or invalidate get/put buffers - if (off == 0 && way == std::ios_base::cur) - return pos_type (gztell (m_file) + computed_off); - - if (way == std::ios_base::beg) - ret = pos_type (gzseek (m_file, computed_off, SEEK_SET)); - else if (way == std::ios_base::cur) - ret = pos_type (gzseek (m_file, computed_off, SEEK_CUR)); - else - // Can't seek from end of a gzipped file, so this will give -1 - ret = pos_type (gzseek (m_file, computed_off, SEEK_END)); - - if (m_io_mode & std::ios_base::in) - // Invalidates contents of the buffer - enable_buffer (); - else - // flush contents of buffer to file - overflow (); - } - - return ret; -} - -gzfilebuf::pos_type -gzfilebuf::seekpos (pos_type sp, std::ios_base::openmode) -{ - pos_type ret = pos_type (off_type (-1)); - - if (this->is_open ()) - { - ret = pos_type (gzseek (m_file, sp, SEEK_SET)); - - if (m_io_mode & std::ios_base::in) - // Invalidates contents of the buffer - enable_buffer (); - else - // flush contents of buffer to file - overflow (); - } - - return ret; -} - -// Default constructor initializes stream buffer -gzifstream::gzifstream () - : std::istream (nullptr), sb () -{ this->init (&sb); } - -// Initialize stream buffer and open file -gzifstream::gzifstream (const char *name, std::ios_base::openmode mode) - : std::istream (nullptr), sb () -{ - this->init (&sb); - this->open (name, mode); -} - -// Initialize stream buffer and attach to file -gzifstream::gzifstream (int fd, std::ios_base::openmode mode) - : std::istream (nullptr), sb () -{ - this->init (&sb); - this->attach (fd, mode); -} - -// Open file and go into fail() state if unsuccessful -void -gzifstream::open (const char *name, std::ios_base::openmode mode) -{ - if (! sb.open (name, mode | std::ios_base::in)) - this->setstate (std::ios_base::failbit); - else - this->clear (); -} - -// Attach to file and go into fail() state if unsuccessful -void -gzifstream::attach (int fd, std::ios_base::openmode mode) -{ - if (! sb.attach (fd, mode | std::ios_base::in)) - this->setstate (std::ios_base::failbit); - else - this->clear (); -} - -// Close file -void -gzifstream::close () -{ - if (! sb.close ()) - this->setstate (std::ios_base::failbit); -} - -// Default constructor initializes stream buffer -gzofstream::gzofstream () - : std::ostream (nullptr), sb () -{ this->init (&sb); } - -// Initialize stream buffer and open file -gzofstream::gzofstream (const char *name, std::ios_base::openmode mode) - : std::ostream (nullptr), sb () -{ - this->init (&sb); - this->open (name, mode); -} - -// Initialize stream buffer and attach to file -gzofstream::gzofstream (int fd, std::ios_base::openmode mode) - : std::ostream (nullptr), sb () -{ - this->init (&sb); - this->attach (fd, mode); -} - -// Open file and go into fail() state if unsuccessful -void -gzofstream::open (const char *name, std::ios_base::openmode mode) -{ - if (! sb.open (name, mode | std::ios_base::out)) - this->setstate (std::ios_base::failbit); - else - this->clear (); -} - -// Attach to file and go into fail() state if unsuccessful -void -gzofstream::attach (int fd, std::ios_base::openmode mode) -{ - if (! sb.attach (fd, mode | std::ios_base::out)) - this->setstate (std::ios_base::failbit); - else - this->clear (); -} - -// Close file -void -gzofstream::close () -{ - if (! sb.close ()) - this->setstate (std::ios_base::failbit); -} - -#endif diff -r a7813409b8c6 -r d809b99f1280 libinterp/corefcn/zfstream.h --- a/libinterp/corefcn/zfstream.h Sun Sep 05 19:34:37 2021 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,509 +0,0 @@ -//////////////////////////////////////////////////////////////////////// -// -// Copyright (C) 2005-2021 The Octave Project Developers -// -// See the file COPYRIGHT.md in the top-level directory of this -// distribution or . -// -// This file is part of Octave. -// -// Octave is free software: you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Octave is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Octave; see the file COPYING. If not, see -// . -// -//////////////////////////////////////////////////////////////////////// - -/* - - This file is adapted from the zlib 1.2.2 contrib/iostream3 code, - written by - - Ludwig Schwardt - original version by Kevin Ruland - -*/ - -#if ! defined (octave_zfsstream_h) -#define octave_zfsstream_h 1 - -#include "octave-config.h" - -#if defined (HAVE_ZLIB) - -#include - -#include "zlib.h" - -/** - * @brief Gzipped file stream buffer class. - * - * This class implements basic_filebuf for gzipped files. It doesn't yet - * support seeking (allowed by zlib but slow/limited), putback and read/write - * access * (tricky). Otherwise, it attempts to be a drop-in replacement for - * the standard file streambuf. -*/ -class gzfilebuf : public std::streambuf -{ -public: - // Default constructor. - gzfilebuf (); - - // No copying! - - gzfilebuf (const gzfilebuf&) = delete; - - gzfilebuf& operator = (const gzfilebuf&) = delete; - - // Destructor. - virtual ~gzfilebuf (); - - /** - * @brief Set compression level and strategy on the fly. - * @param comp_level Compression level (see zlib.h for allowed values) - * @param comp_strategy Compression strategy (see zlib.h for allowed values) - * @return Z_OK on success, Z_STREAM_ERROR otherwise. - * - * Unfortunately, these parameters cannot be modified separately, as the - * previous zfstream version assumed. Since the strategy is seldom changed, - * it can default and setcompression(level) then becomes like the old - * setcompressionlevel(level). - */ - int - setcompression (int comp_level, - int comp_strategy = Z_DEFAULT_STRATEGY); - - /** - * @brief Check if file is open. - * @return True if file is open. - */ - bool - is_open () const { return (m_file != nullptr); } - - /** - * @brief Open gzipped file. - * @param name Filename. - * @param mode Open mode flags. - * @return @c this on success, NULL on failure. - */ - gzfilebuf* - open (const char *name, - std::ios_base::openmode mode); - - /** - * @brief Attach to already open gzipped file. - * @param fd File descriptor. - * @param mode Open mode flags. - * @return @c this on success, NULL on failure. - */ - gzfilebuf* - attach (int fd, - std::ios_base::openmode mode); - - /** - * @brief Close gzipped file. - * @return @c this on success, NULL on failure. - */ - gzfilebuf* - close (); - -protected: - /** - * @brief Convert ios open mode int to mode string used by zlib. - * @return True if valid mode flag combination. - */ - bool - open_mode (std::ios_base::openmode mode, - char *c_mode) const; - - /** - * @brief Number of characters available in stream buffer. - * @return Number of characters. - * - * This indicates number of characters in get area of stream buffer. - * These characters can be read without accessing the gzipped file. - */ - virtual std::streamsize - showmanyc (); - - /** - * @brief Fill get area from gzipped file. - * @return First character in get area on success, EOF on error. - * - * This actually reads characters from gzipped file to stream - * buffer. Always buffered. - */ - virtual int_type - underflow (); - - /** - * @brief Write put area to gzipped file. - * @param c Extra character to add to buffer contents. - * @return Non-EOF on success, EOF on error. - * - * This actually writes characters in stream buffer to - * gzipped file. With unbuffered output this is done one - * character at a time. - */ - virtual int_type - overflow (int_type c = traits_type::eof ()); - - /** - * @brief Installs external stream buffer. - * @param p Pointer to char buffer. - * @param n Size of external buffer. - * @return @c this on success, NULL on failure. - * - * Call setbuf(0,0) to enable unbuffered output. - */ - virtual std::streambuf* - setbuf (char_type *p, - std::streamsize n); - - /** - * @brief Flush stream buffer to file. - * @return 0 on success, -1 on error. - * - * This calls underflow(EOF) to do the job. - */ - virtual int - sync (); - - /** - * @brief Alters the stream positions. - * - * Each derived class provides its own appropriate behavior. - */ - virtual pos_type - seekoff (off_type off, std::ios_base::seekdir way, - std::ios_base::openmode mode = - std::ios_base::in | std::ios_base::out); - - /** - * @brief Alters the stream positions. - * - * Each derived class provides its own appropriate behavior. - */ - virtual pos_type - seekpos (pos_type sp, std::ios_base::openmode mode = - std::ios_base::in | std::ios_base::out); - - virtual int_type - pbackfail (int_type c = traits_type::eof ()); - -// -// Some future enhancements -// -// virtual int_type uflow(); -// virtual int_type pbackfail(int_type c = traits_type::eof()); - -private: - - /** - * @brief Allocate internal buffer. - * - * This function is safe to call multiple times. It will ensure - * that a proper internal buffer exists if it is required. If the - * buffer already exists or is external, the buffer pointers will be - * reset to their original state. - */ - void - enable_buffer (); - - /** - * @brief Destroy internal buffer. - * - * This function is safe to call multiple times. It will ensure - * that the internal buffer is deallocated if it exists. In any - * case, it will also reset the buffer pointers. - */ - void - disable_buffer (); - - /** - * Underlying file pointer. - */ - gzFile m_file; - - /** - * Mode in which file was opened. - */ - std::ios_base::openmode m_io_mode; - - /** - * @brief True if this object owns file descriptor. - * - * This makes the class responsible for closing the file - * upon destruction. - */ - bool m_own_fd; - - /** - * @brief Stream buffer. - * - * For simplicity this remains allocated on the free store for the - * entire life span of the gzfilebuf object, unless replaced by setbuf. - */ - char_type *m_buffer; - - /** - * @brief Stream buffer size. - * - * Defaults to system default buffer size (typically 8192 bytes). - * Modified by setbuf. - */ - std::streamsize m_buffer_size; - - /** - * @brief True if this object owns stream buffer. - * - * This makes the class responsible for deleting the buffer - * upon destruction. - */ - bool m_own_buffer; -}; - -/** - * @brief Gzipped file input stream class. - * - * This class implements ifstream for gzipped files. Seeking and putback - * is not supported yet. -*/ -class gzifstream : public std::istream -{ -public: - // Default constructor - gzifstream (); - - /** - * @brief Construct stream on gzipped file to be opened. - * @param name Filename. - * @param mode Open mode flags (forced to contain ios::in). - */ - explicit - gzifstream (const char *name, - std::ios_base::openmode mode = std::ios_base::in); - - /** - * @brief Construct stream on already open gzipped file. - * @param fd File descriptor. - * @param mode Open mode flags (forced to contain ios::in). - */ - explicit - gzifstream (int fd, - std::ios_base::openmode mode = std::ios_base::in); - - /** - * Obtain underlying stream buffer. - */ - gzfilebuf* - rdbuf () const - { return const_cast(&sb); } - - /** - * @brief Check if file is open. - * @return True if file is open. - */ - bool - is_open () { return sb.is_open (); } - - /** - * @brief Open gzipped file. - * @param name Filename. - * @param mode Open mode flags (forced to contain ios::in). - * - * Stream will be in state good() if file opens successfully; - * otherwise in state fail(). This differs from the behavior of - * ifstream, which never sets the state to good() and therefore - * won't allow you to reuse the stream for a second file unless - * you manually clear() the state. The choice is a matter of - * convenience. - */ - void - open (const char *name, - std::ios_base::openmode mode = std::ios_base::in); - - /** - * @brief Attach to already open gzipped file. - * @param fd File descriptor. - * @param mode Open mode flags (forced to contain ios::in). - * - * Stream will be in state good() if attach succeeded; otherwise - * in state fail(). - */ - void - attach (int fd, - std::ios_base::openmode mode = std::ios_base::in); - - /** - * @brief Close gzipped file. - * - * Stream will be in state fail() if close failed. - */ - void - close (); - -private: - /** - * Underlying stream buffer. - */ - gzfilebuf sb; -}; - -/** - * @brief Gzipped file output stream class. - * - * This class implements ofstream for gzipped files. Seeking and putback - * is not supported yet. -*/ -class gzofstream : public std::ostream -{ -public: - // Default constructor - gzofstream (); - - /** - * @brief Construct stream on gzipped file to be opened. - * @param name Filename. - * @param mode Open mode flags (forced to contain ios::out). - */ - explicit - gzofstream (const char *name, - std::ios_base::openmode mode = std::ios_base::out); - - /** - * @brief Construct stream on already open gzipped file. - * @param fd File descriptor. - * @param mode Open mode flags (forced to contain ios::out). - */ - explicit - gzofstream (int fd, - std::ios_base::openmode mode = std::ios_base::out); - - /** - * Obtain underlying stream buffer. - */ - gzfilebuf* - rdbuf () const - { return const_cast(&sb); } - - /** - * @brief Check if file is open. - * @return True if file is open. - */ - bool - is_open () { return sb.is_open (); } - - /** - * @brief Open gzipped file. - * @param name Filename. - * @param mode Open mode flags (forced to contain ios::out). - * - * Stream will be in state good() if file opens successfully; - * otherwise in state fail(). This differs from the behavior of - * ofstream, which never sets the state to good() and therefore - * won't allow you to reuse the stream for a second file unless - * you manually clear() the state. The choice is a matter of - * convenience. - */ - void - open (const char *name, - std::ios_base::openmode mode = std::ios_base::out); - - /** - * @brief Attach to already open gzipped file. - * @param fd File descriptor. - * @param mode Open mode flags (forced to contain ios::out). - * - * Stream will be in state good() if attach succeeded; otherwise - * in state fail(). - */ - void - attach (int fd, - std::ios_base::openmode mode = std::ios_base::out); - - /** - * @brief Close gzipped file. - * - * Stream will be in state fail() if close failed. - */ - void - close (); - -private: - /** - * Underlying stream buffer. - */ - gzfilebuf sb; -}; - -/** - * @brief Gzipped file output stream manipulator class. - * - * This class defines a two-argument manipulator for gzofstream. It is used - * as base for the setcompression(int,int) manipulator. -*/ -template -class gzomanip2 -{ -public: - // Allows insertor to peek at internals - template - friend gzofstream& - operator<<(gzofstream&, - const gzomanip2&); - - // Constructor - gzomanip2 (gzofstream& (*f)(gzofstream&, T1, T2), - T1 v1, - T2 v2); -private: - // Underlying manipulator function - gzofstream& - (*func)(gzofstream&, T1, T2); - - // Arguments for manipulator function - T1 val1; - T2 val2; -}; - -// Manipulator function thunks through to stream buffer -inline gzofstream& -setcompression (gzofstream& gzs, int l, int s = Z_DEFAULT_STRATEGY) -{ - (gzs.rdbuf ())->setcompression (l, s); - return gzs; -} - -// Manipulator constructor stores arguments -template -inline -gzomanip2::gzomanip2 (gzofstream &(*f)(gzofstream &, T1, T2), - T1 v1, - T2 v2) - : func(f), val1(v1), val2(v2) -{ } - -// Insertor applies underlying manipulator function to stream -template -inline gzofstream& -operator<<(gzofstream& s, const gzomanip2& m) -{ return (*m.func)(s, m.val1, m.val2); } - -// Insert this onto stream to simplify setting of compression level -inline gzomanip2 -setcompression (int l, int s = Z_DEFAULT_STRATEGY) -{ return gzomanip2(&setcompression, l, s); } - -#endif - -#endif