//--------------------------------------------------------- // Copyright 2015 Ontario Institute for Cancer Research // Written by Matei David (matei@cs.toronto.edu) //--------------------------------------------------------- // Original version, https://github.com/mateidavid/zstr, distributed under MIT // license. This file is a combination of the zstr.hpp and strict_fstream.hpp // files in the original src/ directory with additional MFEM modifications. // The MIT License (MIT) // // Copyright (c) 2015 Matei David, Ontario Institute for Cancer Research // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // Reference: // http://stackoverflow.com/questions/14086417/how-to-write-custom-input-stream-in-c #ifndef __ZSTR_HPP #define __ZSTR_HPP #include "../config/config.hpp" #include #include #include #include #include #ifdef MFEM_USE_ZLIB #include #endif // The section below is a modified content of the src/strict_fstream.hpp // file from https://github.com/mateidavid/zstr. /** * This namespace defines wrappers for std::ifstream, std::ofstream, and * std::fstream objects. The wrappers perform the following steps: * - check the open modes make sense * - check that the call to open() is successful * - (for input streams) check that the opened file is peek-able * - turn on the badbit in the exception mask */ namespace strict_fstream { // Overloaded error checks to handle POSIX and GNU strerror_r inline char* check_strerror_r(int r, char* buff, size_t buff_size) { if (r) { snprintf(buff, buff_size, "unknown error: %d", r); } return buff; } inline char* check_strerror_r(char* r, char*, int) { return r; } /// Overload of error-reporting function, to enable use with VS. /// Ref: http://stackoverflow.com/a/901316/717706 static std::string strerror() { std::string buff(80, '\0'); #ifdef _WIN32 if (strerror_s(&buff[0], buff.size(), errno) != 0) { buff = "Unknown error"; } #else char* p = check_strerror_r(strerror_r(errno, &buff[0], buff.size()), &buff[0], buff.size()); std::string tmp(p, std::strlen(p)); std::swap(buff, tmp); #endif buff.resize(buff.find('\0')); return buff; } /// Exception class thrown by failed operations. class Exception : public std::exception { public: Exception(const std::string& msg_) : msg(msg_) {} const char * what() const noexcept { return msg.c_str(); } private: std::string msg; }; // class Exception namespace detail { struct static_method_holder { static std::string mode_to_string(std::ios_base::openmode mode) { static const int n_modes = 6; static const std::ios_base::openmode mode_val_v[n_modes] = { std::ios_base::in, std::ios_base::out, std::ios_base::app, std::ios_base::ate, std::ios_base::trunc, std::ios_base::binary }; static const char * mode_name_v[n_modes] = { "in", "out", "app", "ate", "trunc", "binary" }; std::string res; for (int i = 0; i < n_modes; ++i) { if (mode & mode_val_v[i]) { res += (! res.empty()? "|" : ""); res += mode_name_v[i]; } } if (res.empty()) { res = "none"; } return res; } static void check_mode(const std::string& filename, std::ios_base::openmode mode) { if ((mode & std::ios_base::trunc) && ! (mode & std::ios_base::out)) { throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and not out"); } else if ((mode & std::ios_base::app) && ! (mode & std::ios_base::out)) { throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: app and not out"); } else if ((mode & std::ios_base::trunc) && (mode & std::ios_base::app)) { throw Exception(std::string("strict_fstream: open('") + filename + "'): mode error: trunc and app"); } } static void check_open(std::ios * s_p, const std::string& filename, std::ios_base::openmode mode) { if (s_p->fail()) { throw Exception(std::string("strict_fstream: open('") + filename + "'," + mode_to_string(mode) + "): open failed: " + strerror()); } } static void check_peek(std::istream * is_p, const std::string& filename, std::ios_base::openmode mode) { bool peek_failed = true; try { is_p->peek(); peek_failed = is_p->fail(); } catch (std::ios_base::failure&) {} if (peek_failed) { throw Exception(std::string("strict_fstream: open('") + filename + "'," + mode_to_string(mode) + "): peek failed: " + strerror()); } is_p->clear(); } }; // struct static_method_holder } // namespace detail class ifstream : public std::ifstream { public: ifstream() = default; ifstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in) { open(filename, mode); } void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in) { mode |= std::ios_base::in; exceptions(std::ios_base::badbit); // detail::static_method_holder::check_mode(filename, mode); std::ifstream::open(filename, mode); // detail::static_method_holder::check_open(this, filename, mode); // detail::static_method_holder::check_peek(this, filename, mode); } }; // class ifstream class ofstream : public std::ofstream { public: ofstream() = default; ofstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out) { open(filename, mode); } void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::out) { mode |= std::ios_base::out; exceptions(std::ios_base::badbit); // detail::static_method_holder::check_mode(filename, mode); std::ofstream::open(filename, mode); // detail::static_method_holder::check_open(this, filename, mode); } }; // class ofstream class fstream : public std::fstream { public: fstream() = default; fstream(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in) { open(filename, mode); } void open(const std::string& filename, std::ios_base::openmode mode = std::ios_base::in) { if (! (mode & std::ios_base::out)) { mode |= std::ios_base::in; } exceptions(std::ios_base::badbit); // detail::static_method_holder::check_mode(filename, mode); std::fstream::open(filename, mode); // detail::static_method_holder::check_open(this, filename, mode); // detail::static_method_holder::check_peek(this, filename, mode); } }; // class fstream } // namespace strict_fstream // The section below is a modified content of the src/zstr.hpp file from // https://github.com/mateidavid/zstr. namespace zstr { #ifdef MFEM_USE_ZLIB /// Exception class thrown by failed zlib operations. class Exception : public std::exception { public: Exception(z_stream *zstrm_p, int ret) : msg("zlib: ") { switch (ret) { case Z_STREAM_ERROR: msg += "Z_STREAM_ERROR: "; break; case Z_DATA_ERROR: msg += "Z_DATA_ERROR: "; break; case Z_MEM_ERROR: msg += "Z_MEM_ERROR: "; break; case Z_VERSION_ERROR: msg += "Z_VERSION_ERROR: "; break; case Z_BUF_ERROR: msg += "Z_BUF_ERROR: "; break; default: std::ostringstream oss; oss << ret; msg += "[" + oss.str() + "]: "; break; } msg += zstrm_p->msg; } Exception(const std::string msg_) : msg(msg_) {} const char *what() const noexcept { return msg.c_str(); } private: std::string msg; }; // class Exception #endif #ifdef MFEM_USE_ZLIB namespace detail { class z_stream_wrapper : public z_stream { public: z_stream_wrapper(bool is_input_ = true, int level_ = Z_DEFAULT_COMPRESSION) : is_input(is_input_) { this->zalloc = Z_NULL; this->zfree = Z_NULL; this->opaque = Z_NULL; int ret; if (is_input) { this->avail_in = 0; this->next_in = Z_NULL; ret = inflateInit2(this, 15 + 32); } else { ret = deflateInit2(this, level_, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY); } if (ret != Z_OK) { throw Exception(this, ret); } } ~z_stream_wrapper() { if (is_input) { inflateEnd(this); } else { deflateEnd(this); } } private: bool is_input; }; // class z_stream_wrapper } // namespace detail class istreambuf : public std::streambuf { public: istreambuf(std::streambuf *sbuf_p_, std::size_t buff_size_ = default_buff_size, bool auto_detect_ = true) : sbuf_p(sbuf_p_), zstrm_p(nullptr), buff_size(buff_size_), auto_detect(auto_detect_), auto_detect_run(false), is_text(false) { assert(sbuf_p); in_buff = new char[buff_size]; in_buff_start = in_buff; in_buff_end = in_buff; out_buff = new char[buff_size]; setg(out_buff, out_buff, out_buff); } istreambuf(const istreambuf &) = delete; istreambuf(istreambuf &&) = default; istreambuf &operator=(const istreambuf &) = delete; istreambuf &operator=(istreambuf &&) = default; virtual ~istreambuf() { delete[] in_buff; delete[] out_buff; if (zstrm_p) { delete zstrm_p; } } virtual std::streambuf::int_type underflow() { if (this->gptr() == this->egptr()) { // pointers for free region in output buffer char *out_buff_free_start = out_buff; do { // read more input if none available if (in_buff_start == in_buff_end) { // empty input buffer: refill from the start in_buff_start = in_buff; std::streamsize sz = sbuf_p->sgetn(in_buff, buff_size); in_buff_end = in_buff + sz; if (in_buff_end == in_buff_start) { break; } // end of input } // auto detect if the stream contains text or deflate data if (auto_detect && !auto_detect_run) { auto_detect_run = true; unsigned char b0 = *reinterpret_cast(in_buff_start); unsigned char b1 = *reinterpret_cast(in_buff_start + 1); // Ref: // http://en.wikipedia.org/wiki/Gzip // http://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like is_text = !(in_buff_start + 2 <= in_buff_end && ((b0 == 0x1F && b1 == 0x8B) // gzip header || (b0 == 0x78 && (b1 == 0x01 // zlib header || b1 == 0x9C || b1 == 0xDA)))); } if (is_text) { // simply swap in_buff and out_buff, and adjust pointers assert(in_buff_start == in_buff); std::swap(in_buff, out_buff); out_buff_free_start = in_buff_end; in_buff_start = in_buff; in_buff_end = in_buff; } else { // run inflate() on input if (!zstrm_p) { zstrm_p = new detail::z_stream_wrapper(true); } zstrm_p->next_in = reinterpret_castnext_in)>(in_buff_start); zstrm_p->avail_in = in_buff_end - in_buff_start; zstrm_p->next_out = reinterpret_castnext_out)> (out_buff_free_start); zstrm_p->avail_out = (out_buff + buff_size) - out_buff_free_start; int ret = inflate(zstrm_p, Z_NO_FLUSH); // process return code if (ret != Z_OK && ret != Z_STREAM_END) { throw Exception(zstrm_p, ret); } // update in&out pointers following inflate() in_buff_start = reinterpret_cast(zstrm_p->next_in); in_buff_end = in_buff_start + zstrm_p->avail_in; out_buff_free_start = reinterpret_cast (zstrm_p->next_out); assert(out_buff_free_start + zstrm_p->avail_out == out_buff + buff_size); // if stream ended, deallocate inflator if (ret == Z_STREAM_END) { delete zstrm_p; zstrm_p = nullptr; } } } while (out_buff_free_start == out_buff); // 2 exit conditions: // - end of input: there might or might not be output available // - out_buff_free_start != out_buff: output available this->setg(out_buff, out_buff, out_buff_free_start); } return this->gptr() == this->egptr() ? traits_type::eof() : traits_type::to_int_type(*this->gptr()); } private: std::streambuf *sbuf_p; char *in_buff; char *in_buff_start; char *in_buff_end; char *out_buff; detail::z_stream_wrapper *zstrm_p; std::size_t buff_size; bool auto_detect; bool auto_detect_run; bool is_text; static const std::size_t default_buff_size = (std::size_t)1 << 20; }; // class istreambuf class ostreambuf : public std::streambuf { public: ostreambuf(std::streambuf *sbuf_p_, std::size_t buff_size_ = default_buff_size, int level_ = Z_DEFAULT_COMPRESSION) : sbuf_p(sbuf_p_), zstrm_p(new detail::z_stream_wrapper(false, level_)), buff_size(buff_size_) { assert(sbuf_p); in_buff = new char[buff_size]; out_buff = new char[buff_size]; setp(in_buff, in_buff + buff_size); } ostreambuf(const ostreambuf &) = delete; ostreambuf(ostreambuf &&) = default; ostreambuf &operator=(const ostreambuf &) = delete; ostreambuf &operator=(ostreambuf &&) = default; int deflate_loop(int flush) { while (true) { zstrm_p->next_out = reinterpret_castnext_out)>(out_buff); zstrm_p->avail_out = buff_size; int ret = deflate(zstrm_p, flush); if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) { throw Exception(zstrm_p, ret); } std::streamsize sz = sbuf_p->sputn(out_buff, reinterpret_cast(zstrm_p->next_out) - out_buff); if (sz != reinterpret_cast(zstrm_p->next_out) - out_buff) { // there was an error in the sink stream return -1; } if (ret == Z_STREAM_END || ret == Z_BUF_ERROR || sz == 0) { break; } } return 0; } virtual ~ostreambuf() { // flush the zlib stream // // NOTE: Errors here (sync() return value not 0) are ignored, because we // cannot throw in a destructor. This mirrors the behaviour of // std::basic_filebuf::~basic_filebuf(). To see an exception on error, // close the ofstream with an explicit call to close(), and do not rely // on the implicit call in the destructor. // sync(); delete[] in_buff; delete[] out_buff; delete zstrm_p; } virtual std::streambuf::int_type overflow(std::streambuf::int_type c = traits_type::eof()) { zstrm_p->next_in = reinterpret_castnext_in)>(pbase()); zstrm_p->avail_in = pptr() - pbase(); while (zstrm_p->avail_in > 0) { int r = deflate_loop(Z_NO_FLUSH); if (r != 0) { setp(nullptr, nullptr); return traits_type::eof(); } } setp(in_buff, in_buff + buff_size); return traits_type::eq_int_type(c, traits_type::eof()) ? traits_type::eof() : sputc(c); } virtual int sync() { // first, call overflow to clear in_buff overflow(); if (!pptr()) { return -1; } // then, call deflate asking to finish the zlib stream zstrm_p->next_in = nullptr; zstrm_p->avail_in = 0; if (deflate_loop(Z_FINISH) != 0) { return -1; } deflateReset(zstrm_p); return 0; } private: std::streambuf *sbuf_p; char *in_buff; char *out_buff; detail::z_stream_wrapper *zstrm_p; std::size_t buff_size; static const std::size_t default_buff_size = (std::size_t)1 << 20; }; // class ostreambuf class istream : public std::istream { public: istream(std::istream &is) : std::istream(new istreambuf(is.rdbuf())) { exceptions(std::ios_base::badbit); } explicit istream(std::streambuf *sbuf_p) : std::istream(new istreambuf(sbuf_p)) { exceptions(std::ios_base::badbit); } virtual ~istream() { delete rdbuf(); } }; // class istream class ostream : public std::ostream { public: ostream(std::ostream &os) : std::ostream(new ostreambuf(os.rdbuf())) { exceptions(std::ios_base::badbit); } explicit ostream(std::streambuf *sbuf_p) : std::ostream(new ostreambuf(sbuf_p)) { exceptions(std::ios_base::badbit); } virtual ~ostream() { delete rdbuf(); } }; // class ostream #endif namespace detail { template struct strict_fstream_holder { strict_fstream_holder(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in) : fs_(filename, mode) { } FStream_Type fs_; }; // class strict_fstream_holder } // namespace detail #ifdef MFEM_USE_ZLIB class ifstream : private detail::strict_fstream_holder, public std::istream { public: explicit ifstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::in) : detail::strict_fstream_holder(filename, mode), std::istream(new istreambuf(fs_.rdbuf())) { exceptions(std::ios_base::badbit); } virtual ~ifstream() { if (rdbuf()) { delete rdbuf(); } } }; // class ifstream class ofstream : private detail::strict_fstream_holder, public std::ostream { public: explicit ofstream(const std::string &filename, std::ios_base::openmode mode = std::ios_base::out) : detail::strict_fstream_holder(filename, mode | std::ios_base::binary), std::ostream(new ostreambuf(fs_.rdbuf())) { exceptions(std::ios_base::badbit); } virtual ~ofstream() { if (rdbuf()) { delete rdbuf(); } } }; // class ofstream #endif } // namespace zstr // The section below contains MFEM-specific additions. namespace mfem { class ofgzstream : private zstr::detail::strict_fstream_holder, public std::ostream { public: explicit ofgzstream(const std::string &filename, bool compression = false) : zstr::detail::strict_fstream_holder(filename, std::ios_base::binary), std::ostream(nullptr) { #ifdef MFEM_USE_ZLIB if (compression) { strbuf = new zstr::ostreambuf(fs_.rdbuf()); rdbuf(strbuf); } else #endif { rdbuf(fs_.rdbuf()); } exceptions(std::ios_base::badbit); } explicit ofgzstream(const std::string &filename, char const *open_mode_chars) : zstr::detail::strict_fstream_holder(filename, std::ios_base::binary), std::ostream(nullptr) { #ifdef MFEM_USE_ZLIB // If open_mode_chars contains any combination of open mode chars // containing the 'z' char, compression is enabled. This preserves the // behavior of the old interface but ignores the choice of the compression // level (it is always set to 6). if (std::string(open_mode_chars).find('z') != std::string::npos) { strbuf = new zstr::ostreambuf(fs_.rdbuf()); rdbuf(strbuf); } else #endif { rdbuf(fs_.rdbuf()); } setstate(fs_.rdstate()); exceptions(std::ios_base::badbit); } virtual ~ofgzstream() { delete strbuf; } std::streambuf *strbuf = nullptr; }; class ifgzstream : private zstr::detail::strict_fstream_holder, public std::istream { public: explicit ifgzstream(const std::string &filename) : zstr::detail::strict_fstream_holder(filename, std::ios_base::in), std::istream(nullptr) { #ifdef MFEM_USE_ZLIB strbuf = new zstr::istreambuf(fs_.rdbuf()); rdbuf(strbuf); #else rdbuf(fs_.rdbuf()); #endif setstate(fs_.rdstate()); exceptions(std::ios_base::badbit); } virtual ~ifgzstream() { delete strbuf; } std::streambuf *strbuf = nullptr; }; /// Input file stream that remembers the input file name (useful for example /// when reading NetCDF meshes) and supports optional zlib decompression. class named_ifgzstream : public ifgzstream { public: named_ifgzstream(const std::string &mesh_name) : ifgzstream(mesh_name), filename(mesh_name) {} const std::string filename; }; } // namespace mfem #endif