CS144Lab/libsponge/util/file_descriptor.cc
2021-09-21 17:11:37 -07:00

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));
}