Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:42:45

0001 //
0002 // Copyright (c) 2012 Artyom Beilis (Tonkikh)
0003 // Copyright (c) 2019-2020 Alexander Grund
0004 //
0005 // Distributed under the Boost Software License, Version 1.0.
0006 // https://www.boost.org/LICENSE_1_0.txt
0007 
0008 #ifndef BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
0009 #define BOOST_NOWIDE_FILEBUF_HPP_INCLUDED
0010 
0011 #include <boost/nowide/config.hpp>
0012 #if BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT
0013 #include <boost/nowide/cstdio.hpp>
0014 #include <boost/nowide/detail/is_path.hpp>
0015 #include <boost/nowide/stackstring.hpp>
0016 #include <cassert>
0017 #include <cstdio>
0018 #include <ios>
0019 #include <limits>
0020 #include <locale>
0021 #include <stdexcept>
0022 #include <streambuf>
0023 #else
0024 #include <fstream>
0025 #endif
0026 
0027 namespace boost {
0028 namespace nowide {
0029     namespace detail {
0030         /// Same as std::ftell but potentially with Large File Support
0031         BOOST_NOWIDE_DECL std::streampos ftell(FILE* file);
0032         /// Same as std::fseek but potentially with Large File Support
0033         BOOST_NOWIDE_DECL int fseek(FILE* file, std::streamoff offset, int origin);
0034     } // namespace detail
0035 
0036 #if !BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT && !defined(BOOST_NOWIDE_DOXYGEN)
0037     using std::basic_filebuf;
0038     using std::filebuf;
0039 #else // Windows
0040     ///
0041     /// \brief This forward declaration defines the basic_filebuf type
0042     ///        which is used when #BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT is set, e.g. on Windows.
0043     ///
0044     /// It is implemented and specialized for CharType = char, it
0045     /// implements std::filebuf over standard C I/O
0046     ///
0047     template<typename CharType, typename Traits = std::char_traits<CharType>>
0048     class basic_filebuf;
0049 
0050     ///
0051     /// \brief This is the implementation of std::filebuf
0052     ///        which is used when #BOOST_NOWIDE_USE_FILEBUF_REPLACEMENT is set, e.g. on Windows.
0053     ///
0054     /// It is implemented and specialized for CharType = char, it
0055     /// implements std::filebuf over standard C I/O
0056     ///
0057     template<>
0058     class basic_filebuf<char> : public std::basic_streambuf<char>
0059     {
0060         using Traits = std::char_traits<char>;
0061 
0062     public:
0063 #ifdef BOOST_MSVC
0064 #pragma warning(push)
0065 #pragma warning(disable : 4351) // new behavior : elements of array will be default initialized
0066 #endif
0067         ///
0068         /// Creates new filebuf
0069         ///
0070         basic_filebuf() :
0071             file_(nullptr), buffer_(nullptr), buffer_size_(BUFSIZ), owns_buffer_(false), unbuffered_read_(false),
0072             last_char_(), mode_(std::ios_base::openmode(0))
0073         {
0074             setg(nullptr, nullptr, nullptr);
0075             setp(nullptr, nullptr);
0076         }
0077 #ifdef BOOST_MSVC
0078 #pragma warning(pop)
0079 #endif
0080         basic_filebuf(const basic_filebuf&) = delete;
0081         basic_filebuf& operator=(const basic_filebuf&) = delete;
0082         basic_filebuf(basic_filebuf&& other) noexcept : basic_filebuf()
0083         {
0084             swap(other);
0085         }
0086         basic_filebuf& operator=(basic_filebuf&& other) noexcept
0087         {
0088             close();
0089             swap(other);
0090             return *this;
0091         }
0092         void swap(basic_filebuf& rhs)
0093         {
0094             std::basic_streambuf<char>::swap(rhs);
0095             using std::swap;
0096             swap(file_, rhs.file_);
0097             swap(buffer_, rhs.buffer_);
0098             swap(buffer_size_, rhs.buffer_size_);
0099             swap(owns_buffer_, rhs.owns_buffer_);
0100             swap(unbuffered_read_, rhs.unbuffered_read_);
0101             swap(last_char_[0], rhs.last_char_[0]);
0102             swap(mode_, rhs.mode_);
0103 
0104             // Fixup last_char references
0105             if(pbase() == rhs.last_char_)
0106                 setp(last_char_, (pptr() == epptr()) ? last_char_ : last_char_ + 1);
0107             if(eback() == rhs.last_char_)
0108                 setg(last_char_, (gptr() == rhs.last_char_) ? last_char_ : last_char_ + 1, last_char_ + 1);
0109 
0110             if(rhs.pbase() == last_char_)
0111                 rhs.setp(rhs.last_char_, (rhs.pptr() == rhs.epptr()) ? rhs.last_char_ : rhs.last_char_ + 1);
0112             if(rhs.eback() == last_char_)
0113             {
0114                 rhs.setg(rhs.last_char_,
0115                          (rhs.gptr() == last_char_) ? rhs.last_char_ : rhs.last_char_ + 1,
0116                          rhs.last_char_ + 1);
0117             }
0118         }
0119 
0120         virtual ~basic_filebuf()
0121         {
0122             close();
0123         }
0124 
0125         ///
0126         /// Same as std::filebuf::open but s is UTF-8 string
0127         ///
0128         basic_filebuf* open(const std::string& s, std::ios_base::openmode mode)
0129         {
0130             return open(s.c_str(), mode);
0131         }
0132         ///
0133         /// Same as std::filebuf::open but s is UTF-8 string
0134         ///
0135         basic_filebuf* open(const char* s, std::ios_base::openmode mode)
0136         {
0137             const wstackstring name(s);
0138             return open(name.get(), mode);
0139         }
0140         /// Opens the file with the given name, see std::filebuf::open
0141         basic_filebuf* open(const wchar_t* s, std::ios_base::openmode mode)
0142         {
0143             if(is_open())
0144                 return nullptr;
0145             validate_cvt(this->getloc());
0146             const bool ate = (mode & std::ios_base::ate) != 0;
0147             if(ate)
0148                 mode &= ~std::ios_base::ate;
0149             const wchar_t* smode = get_mode(mode);
0150             if(!smode)
0151                 return nullptr;
0152             file_ = detail::wfopen(s, smode);
0153             if(!file_)
0154                 return nullptr;
0155             if(ate && detail::fseek(file_, 0, SEEK_END) != 0)
0156             {
0157                 close();
0158                 return nullptr;
0159             }
0160             mode_ = mode;
0161             set_unbuffered_read();
0162             return this;
0163         }
0164         template<typename Path>
0165         detail::enable_if_path_t<Path, basic_filebuf*> open(const Path& file_name, std::ios_base::openmode mode)
0166         {
0167             return open(file_name.c_str(), mode);
0168         }
0169         ///
0170         /// Same as std::filebuf::close()
0171         ///
0172         basic_filebuf* close()
0173         {
0174             if(!is_open())
0175                 return nullptr;
0176             bool res = sync() == 0;
0177             if(std::fclose(file_) != 0)
0178                 res = false;
0179             file_ = nullptr;
0180             mode_ = std::ios_base::openmode(0);
0181             if(owns_buffer_)
0182             {
0183                 delete[] buffer_;
0184                 buffer_ = nullptr;
0185                 owns_buffer_ = false;
0186             }
0187             setg(nullptr, nullptr, nullptr);
0188             setp(nullptr, nullptr);
0189             return res ? this : nullptr;
0190         }
0191         ///
0192         /// Same as std::filebuf::is_open()
0193         ///
0194         bool is_open() const
0195         {
0196             return file_ != nullptr;
0197         }
0198 
0199     protected:
0200         std::streambuf* setbuf(char* s, std::streamsize n) override
0201         {
0202             assert(n >= 0);
0203             // Maximum compatibility: Discard all local buffers and use user-provided values
0204             // Users should call sync() before or better use it before any IO is done or any file is opened
0205             setg(nullptr, nullptr, nullptr);
0206             setp(nullptr, nullptr);
0207             if(owns_buffer_)
0208             {
0209                 delete[] buffer_;
0210                 owns_buffer_ = false;
0211             }
0212             buffer_ = s;
0213             buffer_size_ = (n >= 0) ? static_cast<size_t>(n) : 0;
0214             set_unbuffered_read();
0215             return this;
0216         }
0217 
0218         int sync() override
0219         {
0220             if(!file_)
0221                 return 0;
0222             bool result;
0223             if(pptr())
0224             {
0225                 // Only flush if anything was written, otherwise behavior of fflush is undefined. I.e.:
0226                 // - Buffered mode: pptr was set to buffer_ and advanced
0227                 // - Unbuffered mode: pptr set to last_char_
0228                 const bool has_prev_write = pptr() != buffer_;
0229                 result = overflow() != EOF;
0230                 if(has_prev_write && std::fflush(file_) != 0)
0231                     result = false;
0232             } else
0233                 result = stop_reading();
0234             return result ? 0 : -1;
0235         }
0236 
0237         int overflow(int c = EOF) override
0238         {
0239             if(!(mode_ & (std::ios_base::out | std::ios_base::app)))
0240                 return EOF;
0241 
0242             if(!stop_reading())
0243                 return EOF;
0244 
0245             size_t n = pptr() - pbase();
0246             if(n > 0)
0247             {
0248                 if(std::fwrite(pbase(), 1, n, file_) != n)
0249                     return EOF;
0250                 assert(buffer_);
0251                 setp(buffer_, buffer_ + buffer_size_);
0252                 if(c != EOF)
0253                 {
0254                     *buffer_ = Traits::to_char_type(c);
0255                     pbump(1);
0256                 }
0257             } else if(c != EOF)
0258             {
0259                 if(buffer_size_ > 0)
0260                 {
0261                     make_buffer();
0262                     setp(buffer_, buffer_ + buffer_size_);
0263                     *buffer_ = Traits::to_char_type(c);
0264                     pbump(1);
0265                 } else if(std::fputc(c, file_) == EOF)
0266                 {
0267                     return EOF;
0268                 } else if(!pptr())
0269                 {
0270                     // Set to dummy value so we know we have written something
0271                     setp(last_char_, last_char_);
0272                 }
0273             }
0274             return Traits::not_eof(c);
0275         }
0276 
0277         std::streamsize xsputn(const char* s, std::streamsize n) override
0278         {
0279             // Only optimize when writing more than a buffer worth of data
0280             if(n <= static_cast<std::streamsize>(buffer_size_))
0281                 return std::basic_streambuf<char>::xsputn(s, n);
0282             if(!(mode_ & (std::ios_base::out | std::ios_base::app)) || !stop_reading())
0283                 return 0;
0284 
0285             assert(n >= 0);
0286             // First empty the remaining put area, if any
0287             const char* const base = pbase();
0288             const size_t num_buffered = pptr() - base;
0289             if(num_buffered != 0)
0290             {
0291                 const auto num_written = std::fwrite(base, 1, num_buffered, file_);
0292                 setp(const_cast<char*>(base + num_written), epptr()); // i.e. pbump(num_written)
0293                 if(num_written != num_buffered)
0294                     return 0; // Error writing buffered chars
0295             }
0296             // Then write directly to file
0297             const auto num_written = std::fwrite(s, 1, static_cast<size_t>(n), file_);
0298             if(num_written > 0u && base != last_char_)
0299                 setp(last_char_, last_char_); // Mark as "written" if not done yet
0300             return num_written;
0301         }
0302 
0303         int underflow() override
0304         {
0305             if(!(mode_ & std::ios_base::in) || !stop_writing())
0306                 return EOF;
0307             if(unbuffered_read_)
0308             {
0309                 const int c = std::fgetc(file_);
0310                 if(c == EOF)
0311                     return EOF;
0312                 last_char_[0] = Traits::to_char_type(c);
0313                 setg(last_char_, last_char_, last_char_ + 1);
0314             } else
0315             {
0316                 make_buffer();
0317                 const size_t n = std::fread(buffer_, 1, buffer_size_, file_);
0318                 setg(buffer_, buffer_, buffer_ + n);
0319                 if(n == 0)
0320                     return EOF;
0321             }
0322             return Traits::to_int_type(*gptr());
0323         }
0324 
0325         std::streamsize xsgetn(char* s, std::streamsize n) override
0326         {
0327             // Only optimize when reading more than a buffer worth of data
0328             if(n <= static_cast<std::streamsize>(unbuffered_read_ ? 1u : buffer_size_))
0329                 return std::basic_streambuf<char>::xsgetn(s, n);
0330             if(!(mode_ & std::ios_base::in) || !stop_writing())
0331                 return 0;
0332             assert(n >= 0);
0333             std::streamsize num_copied = 0;
0334             // First empty the remaining get area, if any
0335             const auto num_buffered = egptr() - gptr();
0336             if(num_buffered != 0)
0337             {
0338                 const auto num_read = num_buffered > n ? n : num_buffered;
0339                 traits_type::copy(s, gptr(), static_cast<size_t>(num_read));
0340                 s += num_read;
0341                 n -= num_read;
0342                 num_copied = num_read;
0343                 setg(eback(), gptr() + num_read, egptr()); // i.e. gbump(num_read)
0344             }
0345             // Then read directly from file (loop as number of bytes read may be less than requested)
0346             while(n > 0)
0347             {
0348                 const auto num_read = std::fread(s, 1, static_cast<size_t>(n), file_);
0349                 if(num_read == 0) // EOF or error
0350                     break;
0351                 s += num_read;
0352                 n -= num_read;
0353                 num_copied += num_read;
0354             }
0355             return num_copied;
0356         }
0357 
0358         int pbackfail(int c = EOF) override
0359         {
0360             // For simplicity we only allow putting back into our read buffer
0361             // So putting back more chars than we have read from the buffer will fail
0362             if(gptr() > eback())
0363                 gbump(-1);
0364             else
0365                 return EOF;
0366 
0367             // Assign the new value if requested
0368             if(c != EOF && *gptr() != Traits::to_char_type(c))
0369                 *gptr() = Traits::to_char_type(c);
0370             return Traits::not_eof(c);
0371         }
0372 
0373         std::streampos seekoff(std::streamoff off,
0374                                std::ios_base::seekdir seekdir,
0375                                std::ios_base::openmode = std::ios_base::in | std::ios_base::out) override
0376         {
0377             if(!file_)
0378                 return EOF;
0379             // Switching between input<->output requires a seek
0380             // So do NOT optimize for seekoff(0, cur) as No-OP
0381 
0382             // On some implementations a seek also flushes, so do a full sync
0383             if(sync() != 0)
0384                 return EOF;
0385             int whence;
0386             switch(seekdir)
0387             {
0388             case std::ios_base::beg: whence = SEEK_SET; break;
0389             case std::ios_base::cur: whence = SEEK_CUR; break;
0390             case std::ios_base::end: whence = SEEK_END; break;
0391             default: assert(false); return EOF;
0392             }
0393             if(detail::fseek(file_, off, whence) != 0)
0394                 return EOF;
0395             return detail::ftell(file_);
0396         }
0397         std::streampos seekpos(std::streampos pos,
0398                                std::ios_base::openmode m = std::ios_base::in | std::ios_base::out) override
0399         {
0400             // Standard mandates "as-if fsetpos", but assume the effect is the same as fseek
0401             return seekoff(pos, std::ios_base::beg, m);
0402         }
0403         void imbue(const std::locale& loc) override
0404         {
0405             validate_cvt(loc);
0406         }
0407 
0408     private:
0409         void make_buffer()
0410         {
0411             if(buffer_)
0412                 return;
0413             if(buffer_size_ > 0)
0414             {
0415                 buffer_ = new char[buffer_size_];
0416                 owns_buffer_ = true;
0417             }
0418         }
0419 
0420         void set_unbuffered_read()
0421         {
0422             // In text mode we cannot use buffering as we are required to know the (file) position of each
0423             // char in the get area and to seek back in case of a sync to "put back" unread chars.
0424             // However std::fseek with non-zero offsets is unsupported for text files and the (file) offset
0425             // to seek back is unknown anyway due to newlines which may got converted.
0426             unbuffered_read_ = !(mode_ & std::ios_base::binary) || buffer_size_ == 0u;
0427         }
0428 
0429         void validate_cvt(const std::locale& loc)
0430         {
0431             if(!std::use_facet<std::codecvt<char, char, std::mbstate_t>>(loc).always_noconv())
0432                 throw std::runtime_error("Converting codecvts are not supported");
0433         }
0434 
0435         /// Stop reading adjusting the file pointer if necessary
0436         /// Postcondition: gptr() == nullptr
0437         bool stop_reading()
0438         {
0439             if(!gptr())
0440                 return true;
0441             const auto off = gptr() - egptr();
0442             setg(nullptr, nullptr, nullptr);
0443             if(!off)
0444                 return true;
0445 #if defined(__clang__)
0446 #pragma clang diagnostic push
0447 #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
0448 #endif
0449             // coverity[result_independent_of_operands]
0450             if(off < std::numeric_limits<std::streamoff>::min())
0451                 return false;
0452 #if defined(__clang__)
0453 #pragma clang diagnostic pop
0454 #endif
0455             return detail::fseek(file_, static_cast<std::streamoff>(off), SEEK_CUR) == 0;
0456         }
0457 
0458         /// Stop writing. If any bytes are to be written, writes them to file
0459         /// Postcondition: pptr() == nullptr
0460         bool stop_writing()
0461         {
0462             if(pptr())
0463             {
0464                 const char* const base = pbase();
0465                 const size_t n = pptr() - base;
0466                 setp(nullptr, nullptr);
0467                 if(n && std::fwrite(base, 1, n, file_) != n)
0468                     return false;
0469             }
0470             return true;
0471         }
0472 
0473         static const wchar_t* get_mode(std::ios_base::openmode mode)
0474         {
0475             //
0476             // done according to n2914 table 106 27.9.1.4
0477             //
0478 
0479             // note can't use switch case as overload operator can't be used
0480             // in constant expression
0481             if(mode == (std::ios_base::out))
0482                 return L"w";
0483             if(mode == (std::ios_base::out | std::ios_base::app))
0484                 return L"a";
0485             if(mode == (std::ios_base::app))
0486                 return L"a";
0487             if(mode == (std::ios_base::out | std::ios_base::trunc))
0488                 return L"w";
0489             if(mode == (std::ios_base::in))
0490                 return L"r";
0491             if(mode == (std::ios_base::in | std::ios_base::out))
0492                 return L"r+";
0493             if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
0494                 return L"w+";
0495             if(mode == (std::ios_base::in | std::ios_base::out | std::ios_base::app))
0496                 return L"a+";
0497             if(mode == (std::ios_base::in | std::ios_base::app))
0498                 return L"a+";
0499             if(mode == (std::ios_base::binary | std::ios_base::out))
0500                 return L"wb";
0501             if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::app))
0502                 return L"ab";
0503             if(mode == (std::ios_base::binary | std::ios_base::app))
0504                 return L"ab";
0505             if(mode == (std::ios_base::binary | std::ios_base::out | std::ios_base::trunc))
0506                 return L"wb";
0507             if(mode == (std::ios_base::binary | std::ios_base::in))
0508                 return L"rb";
0509             if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out))
0510                 return L"r+b";
0511             if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::trunc))
0512                 return L"w+b";
0513             if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::out | std::ios_base::app))
0514                 return L"a+b";
0515             if(mode == (std::ios_base::binary | std::ios_base::in | std::ios_base::app))
0516                 return L"a+b";
0517             return nullptr;
0518         }
0519 
0520         FILE* file_;
0521         char* buffer_;
0522         size_t buffer_size_;
0523         bool owns_buffer_;
0524         bool unbuffered_read_; // True to read char by char
0525         char last_char_[1];
0526         std::ios::openmode mode_;
0527     };
0528 
0529     ///
0530     /// \brief Convenience typedef
0531     ///
0532     using filebuf = basic_filebuf<char>;
0533 
0534     /// Swap the basic_filebuf instances
0535     template<typename CharType, typename Traits>
0536     void swap(basic_filebuf<CharType, Traits>& lhs, basic_filebuf<CharType, Traits>& rhs)
0537     {
0538         lhs.swap(rhs);
0539     }
0540 
0541 #endif // windows
0542 
0543 } // namespace nowide
0544 } // namespace boost
0545 
0546 #endif