/* This file is part of Jellyfish. Jellyfish 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. Jellyfish 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 Jellyfish. If not, see . */ #ifndef __JELLYFISH_STDIO_FILEBUF_HPP__ #define __JELLYFISH_STDIO_FILEBUF_HPP__ #include #include #include #include #include #include // Attempt to be (mostly) compatible with GCC ext/stdio_filebuf.h // class. Contains code from stdio_filbuf.hpp and // http://www.mr-edd.co.uk/blog/beginners_guide_streambuf. It is only // meant as a quick replacement when stdio_filebuf is not available. namespace jellyfish { template > class stdio_filebuf : public std::basic_streambuf<_CharT, _Traits> { const int fd_; FILE* const file_; const std::ios_base::openmode mode_; const size_t put_back_; std::vector<_CharT> buffer_; public: // Types: typedef _CharT char_type; typedef _Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; // typedef std::size_t size_t; /** * @param __fd An open file descriptor. * @param __mode Same meaning as in a standard filebuf. * @param __size Optimal or preferred size of internal buffer, * in chars. * * This constructor associates a file stream buffer with an open * POSIX file descriptor. The file descriptor will be automatically * closed when the stdio_filebuf is closed/destroyed. */ stdio_filebuf(int __fd, std::ios_base::openmode __mode, size_t __size = static_cast(BUFSIZ), size_t put_back = 1) : fd_(__fd), file_(0), mode_(__mode), put_back_(std::max(put_back, (size_t)1)), buffer_(std::max(__size, put_back_) + put_back_) { _CharT* end = buffer_.data() + buffer_.size(); this->setg(end, end, end); } /** * @param __f An open @c FILE*. * @param __mode Same meaning as in a standard filebuf. * @param __size Optimal or preferred size of internal buffer, * in chars. Defaults to system's @c BUFSIZ. * * This constructor associates a file stream buffer with an open * C @c FILE*. The @c FILE* will not be automatically closed when the * stdio_filebuf is closed/destroyed. */ stdio_filebuf(FILE* __f, std::ios_base::openmode __mode, size_t __size = static_cast(BUFSIZ), size_t put_back = 1) : fd_(-1), file_(__f), mode_(__mode), put_back_(std::max(put_back, (size_t)1)), buffer_(std::max(__size, put_back_) + put_back_) { _CharT* end = buffer_.data() + buffer_.size(); this->setg(end, end, end); } /** * Closes the external data stream if the file descriptor constructor * was used. */ virtual ~stdio_filebuf() { if(fd_ != -1) close(fd_); } /** * @return The underlying file descriptor. * * Once associated with an external data stream, this function can be * used to access the underlying POSIX file descriptor. Note that * there is no way for the library to track what you do with the * descriptor, so be careful. */ int fd() { return fd_ != -1 ? fd_ : fileno(file_); } /** * @return The underlying FILE*. * * This function can be used to access the underlying "C" file pointer. * Note that there is no way for the library to track what you do * with the file, so be careful. */ FILE* file() { if(file_) return file_; const char* str_mode; if(mode_ & std::ios_base::app) { str_mode = "a+"; } else if(mode_ & std::ios_base::ate) { str_mode = "a"; } else if(mode_ & std::ios_base::in) { str_mode = (mode_ & std::ios_base::out) ? "r+" : "r"; } else if(mode_ & std::ios_base::out) { str_mode = "w"; } return fdopen(fd_, str_mode); } private: int_type underflow() { if(this->gptr() >= this->egptr()) { _CharT *base = buffer_.data(); _CharT *start = base; if (this->eback() == base) { // Make arrangements for putback characters std::memcpy(base, this->egptr() - put_back_, put_back_ * sizeof(_CharT)); start += put_back_; } // start is now the start of the buffer, proper. // Read from fptr_ in to the provided buffer const ssize_t n = (fd_ != -1) ? read(fd_, start, (buffer_.size() - (start - base)) * sizeof(_CharT)) : std::fread(start, sizeof(_CharT), buffer_.size() - (start - base), file_); if (n <= 0) return _Traits::eof(); // Set buffer pointers this->setg(base, start, start + n); } return _Traits::to_int_type(*this->gptr()); } }; } // namespace jellyfish { #endif // __JELLYFISH_STDIO_FILEBUF_HPP__