111 lines
3.3 KiB
C++
111 lines
3.3 KiB
C++
#include "file_descriptor.hh"
|
|
|
|
#include "util.hh"
|
|
|
|
#include <algorithm>
|
|
#include <fcntl.h>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
#include <sys/uio.h>
|
|
#include <unistd.h>
|
|
|
|
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<FDWrapper>(fd)) {}
|
|
|
|
//! Private constructor used by duplicate()
|
|
FileDescriptor::FileDescriptor(shared_ptr<FDWrapper> 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<ssize_t>(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));
|
|
}
|