Skip to content
Snippets Groups Projects
Commit aad7cf00 authored by Martin Marinov's avatar Martin Marinov Committed by GitHub Enterprise
Browse files

MTBR-925 Add BufferedOutputFile (#79)

Add buffered text output file for faster text writes. Improve platform compatibility for printing.
parent 96a67978
No related branches found
No related tags found
1 merge request!14Merge latest changes to Base from ReForm
// (C) Copyright 2022 by Autodesk, Inc.
#ifndef BASE_BUFFEREDOUTPUTFILE_HH_INCLUDED
#define BASE_BUFFEREDOUTPUTFILE_HH_INCLUDED
#include <Base/Config/BaseDefines.hh>
#include <Base/Utils/IOutputStream.hh>
#include <Base/Debug/DebOut.hh>
#include <cstdio>
#include <vector>
namespace Base
{
// Provide buffered text print operations to a FILE, helps with the performance
// when storing large text data.
class BufferedOutputFile
{
public:
BufferedOutputFile(const char* const _flnm, // filename
const size_t _flsh_size = 1 << 20, // buffer size that forces flush (1Mb)
const size_t _ovfl_size = 1 << 10 // "overflow" space in the buffer, also
// sets the maximum amount of characters that can be printed at once (1Kb)
)
: flsh_size_(_flsh_size), ovfl_size_(_ovfl_size),
bffr_(flsh_size_ + ovfl_size_), bffr_end_(bffr_.data())
{
const char* const MODE = "wb"; // open as write binary
#ifdef _MSC_VER
fopen_s(&file_, _flnm, MODE);
#else
file_ = std::fopen(_flnm, MODE);
#endif
}
~BufferedOutputFile()
{
if (!opened())
return;
flush();
fclose(file_);
}
bool opened() const { return file_ != nullptr; }
#define DEB_check_opened \
DEB_error_if(!opened(), "Write access to a file that has failed open")
void flush()
{
DEB_check_opened;
const auto bffr_size = buffer_size();
if (bffr_size == 0)
return;
fwrite(bffr_.data(), sizeof(char), bffr_size, file_);
bffr_end_ = bffr_.data();
}
template <typename... ArgT> void print(const ArgT&... _args)
{
DEB_check_opened;
const auto char_nmbr = Base::print(bffr_end_, ovfl_size_, _args...);
DEB_error_if(char_nmbr < 0 || char_nmbr >= static_cast<int>(ovfl_size_),
"print() result = "
<< char_nmbr << " is unclear, consider using larger overflow size");
bffr_end_ += char_nmbr;
if (buffer_size() + ovfl_size_ >= bffr_.size()) // make sure we always have
flush(); // at least ovfl_size_ space in the buffer
}
template <typename T> void write(const T* const _ptr, const size_t _nmbr)
{
DEB_check_opened;
flush();
fwrite(_ptr, sizeof(T), _nmbr, file_);
}
// Access the file, but make sure we have flushed first
FILE* file()
{
DEB_check_opened;
flush();
return file_;
}
#undef DEB_check_opened
private:
const size_t flsh_size_; // number of bytes to trigger flushing the buffer
const size_t ovfl_size_; // number of bytes to print in a single print() call
std::vector<char> bffr_; // buffer
char* bffr_end_; // end of the buffer
FILE* file_ = nullptr;
size_t buffer_size() const { return bffr_end_ - bffr_.data(); }
};
} // namespace Base
#endif // BASE_BUFFEREDOUTPUTFILE_HH_INCLUDED
set(my_headers
${CMAKE_CURRENT_SOURCE_DIR}/BaseError.hh
${CMAKE_CURRENT_SOURCE_DIR}/BaseErrorInc.hh
${CMAKE_CURRENT_SOURCE_DIR}/BufferedOutputFile.hh
${CMAKE_CURRENT_SOURCE_DIR}/Environment.hh
${CMAKE_CURRENT_SOURCE_DIR}/FileOutput.hh
${CMAKE_CURRENT_SOURCE_DIR}/IOutputStream.hh
......
// (C) Copyright 2019 by Autodesk, Inc.
// (C) Copyright 2022 by Autodesk, Inc.
#include "Base/Security/Mandatory.hh"
#include "OStringStream.hh"
......@@ -26,16 +26,22 @@ namespace Base
namespace
{
// Cross-platform approach to avoid buffer overflow when printing. However,
// there are subtle differences between these functions, see here for details:
// https://stackoverflow.com/questions/46485639/what-is-the-difference-between-vsnprintf-and-vsprintf-s
#ifdef _MSC_VER
#define VSNPRINTF vsprintf_s
#else
#define VSNPRINTF vsnprintf
#endif
template <size_t _bffr_size>
int sprintf_s(char (&_bffr)[_bffr_size], const char* _frmt, ...)
{
va_list arg_ptr;
va_start(arg_ptr, _frmt);
#ifdef _MSC_VER
int res = vsprintf_s(_bffr, _bffr_size, _frmt, arg_ptr);
#else
int res = vsprintf(_bffr, _frmt, arg_ptr);
#endif // _MSC_VER
int res = VSNPRINTF(_bffr, _bffr_size, _frmt, arg_ptr);
va_end(arg_ptr);
return res;
}
......@@ -46,11 +52,7 @@ int print(char* _bffr, const size_t _bffr_size, const char* _frmt, ...)
{
va_list arg_ptr;
va_start(arg_ptr, _frmt);
#ifdef _MSC_VER
int res = vsprintf_s(_bffr, _bffr_size, _frmt, arg_ptr);
#else
int res = vsprintf(_bffr, _frmt, arg_ptr);
#endif
int res = VSNPRINTF(_bffr, _bffr_size, _frmt, arg_ptr);
va_end(arg_ptr);
return res;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment