#include "file_descriptor.hh" #include "util.hh" #include #include #include #include #include #include using namespace std; //! \param[in] fd is the file descriptor number returned by [open(2)](\ref man2::open) or similar FileDescriptor::FDWrapper::FDWrapper(const int fd) : _fd(fd) { if (fd < 0) { throw runtime_error("invalid fd number:" + to_string(fd)); } } void FileDescriptor::FDWrapper::close() { SystemCall("close", ::close(_fd)); _eof = _closed = true; } FileDescriptor::FDWrapper::~FDWrapper() { try { if (_closed) { return; } close(); } catch (const exception &e) { // don't throw an exception from the destructor std::cerr << "Exception destructing FDWrapper: " << e.what() << std::endl; } } //! \param[in] fd is the file descriptor number returned by [open(2)](\ref man2::open) or similar FileDescriptor::FileDescriptor(const int fd) : _internal_fd(make_shared(fd)) {} //! Private constructor used by duplicate() FileDescriptor::FileDescriptor(shared_ptr other_shared_ptr) : _internal_fd(move(other_shared_ptr)) {} //! \returns a copy of this FileDescriptor FileDescriptor FileDescriptor::duplicate() const { return FileDescriptor(_internal_fd); } //! \param[in] limit is the maximum number of bytes to read; fewer bytes may be returned //! \param[out] str is the string to be read void FileDescriptor::read(std::string &str, const size_t limit) { constexpr size_t BUFFER_SIZE = 1024 * 1024; // maximum size of a read const size_t size_to_read = min(BUFFER_SIZE, limit); str.resize(size_to_read); ssize_t bytes_read = SystemCall("read", ::read(fd_num(), str.data(), size_to_read)); if (limit > 0 && bytes_read == 0) { _internal_fd->_eof = true; } if (bytes_read > static_cast(size_to_read)) { throw runtime_error("read() read more than requested"); } str.resize(bytes_read); register_read(); } //! \param[in] limit is the maximum number of bytes to read; fewer bytes may be returned //! \returns a vector of bytes read string FileDescriptor::read(const size_t limit) { string ret; read(ret, limit); return ret; } size_t FileDescriptor::write(BufferViewList buffer, const bool write_all) { size_t total_bytes_written = 0; do { auto iovecs = buffer.as_iovecs(); const ssize_t bytes_written = SystemCall("writev", ::writev(fd_num(), iovecs.data(), iovecs.size())); if (bytes_written == 0 and buffer.size() != 0) { throw runtime_error("write returned 0 given non-empty input buffer"); } if (bytes_written > ssize_t(buffer.size())) { throw runtime_error("write wrote more than length of input buffer"); } register_write(); buffer.remove_prefix(bytes_written); total_bytes_written += bytes_written; } while (write_all and buffer.size()); return total_bytes_written; } void FileDescriptor::set_blocking(const bool blocking_state) { int flags = SystemCall("fcntl", fcntl(fd_num(), F_GETFL)); if (blocking_state) { flags ^= (flags & O_NONBLOCK); } else { flags |= O_NONBLOCK; } SystemCall("fcntl", fcntl(fd_num(), F_SETFL, flags)); }