2
0
mirror of https://github.com/boostorg/nowide.git synced 2026-02-22 03:22:32 +00:00

Rework filebuf

Check each function against C++ standard and remove redundant operations, especially fflush calls.
This commit is contained in:
Alexander Grund
2019-12-01 21:17:46 +01:00
parent 8a8f23a444
commit 97dba5bc1e

View File

@@ -1,5 +1,6 @@
//
// Copyright (c) 2012 Artyom Beilis (Tonkikh)
// Copyright (c) 2019 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -11,8 +12,11 @@
#include <boost/config.hpp>
#ifdef BOOST_WINDOWS
#include <boost/nowide/stackstring.hpp>
#include <cassert>
#include <cstdio>
#include <iosfwd>
#include <limits>
#include <locale>
#include <stdexcept>
#include <streambuf>
#else
@@ -53,11 +57,13 @@ namespace nowide {
basic_filebuf(const basic_filebuf<char> &);
basic_filebuf &operator=(const basic_filebuf<char> &);
typedef std::char_traits<char> Traits;
public:
///
/// Creates new filebuf
///
basic_filebuf() : buffer_size_(4), buffer_(0), file_(0), own_(true)
basic_filebuf() : buffer_size_(4), buffer_(0), file_(0), owns_buffer_(false), last_char_(0), mode_(std::ios_base::openmode(0))
{
setg(0, 0, 0);
setp(0, 0);
@@ -66,8 +72,6 @@ namespace nowide {
virtual ~basic_filebuf()
{
close();
if(own_ && buffer_)
delete[] buffer_;
}
///
@@ -85,26 +89,28 @@ namespace nowide {
if(is_open())
return NULL;
validate_cvt(this->getloc());
bool ate = bool(mode & std::ios_base::ate);
bool const ate = bool(mode & std::ios_base::ate);
if(ate)
mode = mode ^ std::ios_base::ate;
wchar_t const *smode = get_mode(mode);
if(!smode)
return 0;
#ifdef BOOST_WINDOWS
wstackstring const name(s);
#ifdef BOOST_NOWIDE_FSTREAM_TESTS
FILE *f = ::fopen(s, boost::nowide::narrow(smode).c_str());
file_ = ::_wfopen(name.c_str(), smode);
#else
FILE *f = ::_wfopen(name.c_str(), smode);
short_stackstring smode2(smode);
file_ = std::fopen(s, smode2.c_str());
#endif
if(!f)
if(!file_)
return 0;
if(ate && fseek(f, 0, SEEK_END) != 0)
if(ate && std::fseek(file_, 0, SEEK_END) != 0)
{
fclose(f);
close();
return 0;
}
file_ = f;
mode_ = mode;
return this;
}
///
@@ -112,21 +118,27 @@ namespace nowide {
///
basic_filebuf *close()
{
if(!is_open())
return NULL;
bool res = sync() == 0;
if(file_)
if(std::fclose(file_) != 0)
res = false;
file_ = NULL;
mode_ = std::ios_base::openmode(0);
if(owns_buffer_)
{
if(::fclose(file_) != 0)
res = false;
file_ = 0;
delete[] buffer_;
buffer_ = NULL;
owns_buffer_ = false;
}
return res ? this : 0;
return res ? this : NULL;
}
///
/// Same as std::filebuf::is_open()
///
bool is_open() const
{
return file_ != 0;
return file_ != NULL;
}
private:
@@ -137,7 +149,7 @@ namespace nowide {
if(buffer_size_ > 0)
{
buffer_ = new char[buffer_size_];
own_ = true;
owns_buffer_ = true;
}
}
void validate_cvt(const std::locale &loc)
@@ -149,106 +161,145 @@ namespace nowide {
protected:
virtual std::streambuf *setbuf(char *s, std::streamsize n)
{
if(!buffer_ && n >= 0)
{
buffer_ = s;
buffer_size_ = n;
own_ = false;
}
assert(n >= 0);
// Maximum compatibility: Discard all local buffers and use user-provided values
// Users should call sync() before or better use it before any IO is done or any file is opened
setg(NULL, NULL, NULL);
setp(NULL, NULL);
if(owns_buffer_)
delete[] buffer_;
buffer_ = s;
buffer_size_ = (n >= 0) ? n : 0;
return this;
}
int overflow(int c)
virtual int overflow(int c = EOF)
{
if(!file_)
if(!(mode_ & std::ios_base::out))
return EOF;
if(fixg() < 0)
if(!stop_reading())
return EOF;
size_t n = pptr() - pbase();
if(n > 0)
{
if(::fwrite(pbase(), 1, n, file_) < n)
if(std::fwrite(pbase(), 1, n, file_) != n)
return -1;
fflush(file_);
}
if(buffer_size_ > 0)
{
make_buffer();
setp(buffer_, buffer_ + buffer_size_);
if(c != EOF)
sputc(c);
{
*buffer_ = c;
pbump(1);
}
} else if(c != EOF)
{
if(::fputc(c, file_) == EOF)
if(buffer_size_ > 0)
{
make_buffer();
setp(buffer_, buffer_ + buffer_size_);
*buffer_ = c;
pbump(1);
} else if(std::fputc(c, file_) == EOF)
return EOF;
fflush(file_);
}
return 0;
return Traits::not_eof(c);
}
int sync()
{
return overflow(EOF);
}
int underflow()
virtual int sync()
{
if(!file_)
return 0;
bool result = (pptr()) ? overflow() != EOF : stop_reading();
if(std::fflush(file_) != 0)
return -1;
return result ? 0 : -1;
}
virtual int underflow()
{
if(!(mode_ & std::ios_base::in))
return EOF;
if(fixp() < 0)
if(!stop_writing())
return EOF;
if(buffer_size_ == 0)
{
int c = ::fgetc(file_);
int const c = std::fgetc(file_);
if(c == EOF)
{
return EOF;
}
last_char_ = c;
setg(&last_char_, &last_char_, &last_char_ + 1);
return c;
} else
{
make_buffer();
size_t const n = std::fread(buffer_, 1, buffer_size_, file_);
setg(buffer_, buffer_, buffer_ + n);
if(n == 0)
return EOF;
}
make_buffer();
size_t n = ::fread(buffer_, 1, buffer_size_, file_);
setg(buffer_, buffer_, buffer_ + n);
if(n == 0)
return EOF;
return std::char_traits<char>::to_int_type(*gptr());
return Traits::to_int_type(*gptr());
}
int pbackfail(int)
virtual int pbackfail(int c = EOF)
{
return pubseekoff(-1, std::ios::cur);
if(!(mode_ & std::ios_base::in))
return EOF;
if(!stop_writing())
return EOF;
if(gptr() > eback())
gbump(-1);
else if(seekoff(-1, std::ios_base::cur) != std::streampos(std::streamoff(-1)))
{
if(underflow() == EOF)
return EOF;
} else
return EOF;
// Case 1: Caller just wanted space for 1 char
if(c == EOF)
return Traits::not_eof(c);
// Case 2: Caller wants to put back different char
// gptr now points to the (potentially newly read) previous char
if(*gptr() != c)
*gptr() = Traits::to_char_type(c);
return Traits::not_eof(c);
}
std::streampos seekoff(std::streamoff off, std::ios_base::seekdir seekdir, std::ios_base::openmode /*m*/)
virtual std::streampos
seekoff(std::streamoff off, std::ios_base::seekdir seekdir, std::ios_base::openmode = std::ios_base::in | std::ios_base::out)
{
if(!file_)
return EOF;
if(fixp() < 0 || fixg() < 0)
// Switching between input<->output requires a seek
// So do NOT optimize for seekoff(0, cur) as No-OP
// On some implementations a seek also flushes, so do a full sync
if(sync() != 0)
return EOF;
if(seekdir == std::ios_base::cur)
int whence;
switch(seekdir)
{
if(::fseek(file_, off, SEEK_CUR) < 0)
return EOF;
} else if(seekdir == std::ios_base::beg)
{
if(::fseek(file_, off, SEEK_SET) < 0)
return EOF;
} else if(seekdir == std::ios_base::end)
{
if(::fseek(file_, off, SEEK_END) < 0)
return EOF;
} else
return -1;
return ftell(file_);
case std::ios_base::beg: whence = SEEK_SET; break;
case std::ios_base::cur: whence = SEEK_CUR; break;
case std::ios_base::end: whence = SEEK_END; break;
default: assert(false); return EOF;
}
if(std::fseek(file_, off, whence) != 0)
return EOF;
return std::ftell(file_);
}
std::streampos seekpos(std::streampos off, std::ios_base::openmode m)
virtual std::streampos seekpos(std::streampos pos, std::ios_base::openmode = std::ios_base::in | std::ios_base::out)
{
return seekoff(std::streamoff(off), std::ios_base::beg, m);
if(!file_)
return EOF;
// On some implementations a seek also flushes, so do a full sync
if(sync() != 0)
return EOF;
std::streamoff off = static_cast<std::streamoff>(pos);
if(std::fsetpos(file_, &off) != 0)
return EOF;
assert(std::ftell(file_) == off);
return pos;
}
virtual void imbue(const std::locale &loc)
{
@@ -256,28 +307,34 @@ namespace nowide {
}
private:
int fixg()
/// Stop reading adjusting the file pointer if neccessary
/// Postcondition: gptr() == NULL
bool stop_reading()
{
if(gptr() != egptr())
if(gptr())
{
std::streamsize off = gptr() - egptr();
std::streamsize const off = gptr() - egptr();
setg(0, 0, 0);
if(fseek(file_, off, SEEK_CUR) != 0)
return -1;
assert(off <= std::numeric_limits<long>::max());
if(off && std::fseek(file_, off, SEEK_CUR) != 0)
return false;
}
setg(0, 0, 0);
return 0;
return true;
}
int fixp()
/// Stop writing. If any bytes are to be written, writes them to file
/// Postcondition: pptr() == NULL
bool stop_writing()
{
if(pptr() != 0)
if(pptr())
{
int r = sync();
const char *const base = pbase();
size_t const n = pptr() - base;
setp(0, 0);
return r;
if(n && std::fwrite(base, 1, n, file_) != n)
return false;
}
return 0;
return true;
}
void reset(FILE *f = 0)
@@ -341,8 +398,9 @@ namespace nowide {
size_t buffer_size_;
char *buffer_;
FILE *file_;
bool own_;
bool owns_buffer_;
char last_char_;
std::ios::openmode mode_;
};
///