/* Like std::ofstream but without being incredibly slow. Backed by a raw fd. * Does not support many data types. Currently, it's targeted at writing ARPA * files quickly. */ #ifndef UTIL_FAKE_OFSTREAM_H #define UTIL_FAKE_OFSTREAM_H #include "util/double-conversion/double-conversion.h" #include "util/double-conversion/utils.h" #include "util/file.hh" #include "util/scoped.hh" #include "util/string_piece.hh" #define BOOST_LEXICAL_CAST_ASSUME_C_LOCALE #include namespace util { class FakeOFStream { public: // Does not take ownership of out. // Allows default constructor, but must call SetFD. explicit FakeOFStream(int out = -1, std::size_t buffer_size = 1048576) : buf_(util::MallocOrThrow(buffer_size)), builder_(static_cast(buf_.get()), buffer_size), // Mostly the default but with inf instead. And no flags. convert_(double_conversion::DoubleToStringConverter::NO_FLAGS, "inf", "NaN", 'e', -6, 21, 6, 0), fd_(out), buffer_size_(buffer_size) {} ~FakeOFStream() { if (buf_.get()) Flush(); } void SetFD(int to) { if (builder_.position()) Flush(); fd_ = to; } FakeOFStream &operator<<(float value) { // Odd, but this is the largest number found in the comments. EnsureRemaining(double_conversion::DoubleToStringConverter::kMaxPrecisionDigits + 8); convert_.ToShortestSingle(value, &builder_); return *this; } FakeOFStream &operator<<(double value) { EnsureRemaining(double_conversion::DoubleToStringConverter::kMaxPrecisionDigits + 8); convert_.ToShortest(value, &builder_); return *this; } FakeOFStream &operator<<(StringPiece str) { if (str.size() > buffer_size_) { Flush(); util::WriteOrThrow(fd_, str.data(), str.size()); } else { EnsureRemaining(str.size()); builder_.AddSubstring(str.data(), str.size()); } return *this; } // Inefficient! TODO: more efficient implementation FakeOFStream &operator<<(unsigned value) { return *this << boost::lexical_cast(value); } FakeOFStream &operator<<(char c) { EnsureRemaining(1); builder_.AddCharacter(c); return *this; } // Note this does not sync. void Flush() { util::WriteOrThrow(fd_, buf_.get(), builder_.position()); builder_.Reset(); } // Not necessary, but does assure the data is cleared. void Finish() { Flush(); // It will segfault trying to null terminate otherwise. builder_.Finalize(); buf_.reset(); util::FSyncOrThrow(fd_); } private: void EnsureRemaining(std::size_t amount) { if (static_cast(builder_.size() - builder_.position()) <= amount) { Flush(); } } util::scoped_malloc buf_; double_conversion::StringBuilder builder_; double_conversion::DoubleToStringConverter convert_; int fd_; const std::size_t buffer_size_; }; } // namespace #endif