diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-21 12:27:20 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-21 12:27:20 +0100 |
| commit | 243d958df14b85788232aca623b83826115a5eb9 (patch) | |
| tree | d1f698296b053359a5563731cda8c51df9ab9a6c /modules/fd.ccm | |
| parent | 8a36eb498e1a1c2cf2e886356faa4ce67e52e874 (diff) | |
| download | nihil-243d958df14b85788232aca623b83826115a5eb9.tar.gz nihil-243d958df14b85788232aca623b83826115a5eb9.tar.bz2 | |
rename modules/ to nihil/
Diffstat (limited to 'modules/fd.ccm')
| -rw-r--r-- | modules/fd.ccm | 309 |
1 files changed, 0 insertions, 309 deletions
diff --git a/modules/fd.ccm b/modules/fd.ccm deleted file mode 100644 index ad96ea7..0000000 --- a/modules/fd.ccm +++ /dev/null @@ -1,309 +0,0 @@ -/* - * This source code is released into the public domain. - */ - -module; - -#include <fcntl.h> -#include <unistd.h> - -#include <expected> -#include <format> -#include <stdexcept> -#include <system_error> - -export module nihil:fd; - -import :generic_error; - -namespace nihil { - -/* - * Exception thrown when an internal fd error occurs. This is not supposed - * to be caught, since it indicates an internal logic error in the caller. - */ -export struct fd_logic_error final : std::logic_error { - fd_logic_error(std::string what) - : std::logic_error(std::move(what)) - {} -}; - -/* - * fd: a file descriptor. - */ - -export struct fd final { - // Construct an empty (invalid) fd. - fd() noexcept = default; - - // Construct an fd from an exising file destrictor, taking ownership. - fd(int fd_) noexcept : _fd(fd_) {} - - // Destructor. Close the fd, discarding any errors. - ~fd() - { - if (*this) - this->close(); - } - - // Move from another fd, leaving the moved-from fd in an invalid state. - fd(fd &&other) noexcept - : _fd(std::exchange(other._fd, _invalid_fd)) - {} - - // Move assign from another fd. - auto operator=(fd &&other) noexcept -> fd & - { - if (this != &other) - _fd = std::exchange(other._fd, _invalid_fd); - return *this; - } - - // Not copyable. - fd(fd const &) = delete; - fd& operator=(fd const &) = delete; - - // Return true if this fd is valid (open). - explicit operator bool(this fd const &self) noexcept - { - return self._fd != _invalid_fd; - } - - // Close the wrapped fd. - auto close(this fd &self) -> std::expected<void, std::error_code> - { - auto const ret = ::close(self.get()); - self._fd = _invalid_fd; - - if (ret == 0) - return {}; - - return std::unexpected(std::make_error_code(std::errc(errno))); - } - - // Return the stored fd. - auto get(this fd const &self) -> int { - if (self) - return self._fd; - throw fd_logic_error("Attempt to call get() on invalid fd"); - } - - - // Release the stored fd and return it. The caller must close it. - auto release(this fd &&self) -> int { - if (self) - return std::exchange(self._fd, self._invalid_fd); - throw fd_logic_error("Attempt to release an invalid fd"); - } - -private: - static constexpr int _invalid_fd = -1; - - int _fd = _invalid_fd; -}; - -// Create a copy of this fd by calling dup(). -export auto dup(fd const &self) -> std::expected<fd, std::error_code> -{ - auto thisfd = self.get(); - - auto const newfd = ::dup(thisfd); - if (newfd != -1) - return {newfd}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Create a copy of this fd by calling dup2(). Note that because this results -// in the existing fd and the new fd both being managed by an fd instance, -// there are two potential cases that can cause problems: -// -// - dup()ing an fd to itself (a no-op) -// - dup()ing an fd to an fd which is already managed by an fd instance -// -// In both of these cases, either use raw_dup() instead, or immediately call -// release() on the returned fd to prevent the fd instance from closing it. -export auto dup(fd const &self, int newfd) - -> std::expected<fd, std::error_code> -{ - auto thisfd = self.get(); - - auto const ret = ::dup2(thisfd, newfd); - if (ret != -1) - return {newfd}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Create a copy of this fd by calling dup(). -export auto raw_dup(fd const &self) -> std::expected<int, std::error_code> -{ - auto thisfd = self.get(); - - auto const newfd = ::dup(thisfd); - if (newfd != -1) - return {newfd}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Create a copy of this fd by calling dup2(). -export auto raw_dup(fd const &self, int newfd) - -> std::expected<int, std::error_code> -{ - auto thisfd = self.get(); - - auto const ret = ::dup2(thisfd, newfd); - if (ret != -1) - return {newfd}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Return the fnctl flags for this fd. -export auto getflags(fd const &self) -> std::expected<int, std::error_code> -{ - auto const flags = ::fcntl(self.get(), F_GETFL); - if (flags != -1) - return {flags}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Replace the fnctl flags for this fd. -export auto replaceflags(fd &self, int newflags) - -> std::expected<void, std::error_code> -{ - auto const ret = ::fcntl(self.get(), F_SETFL, newflags); - if (ret == 0) - return {}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Add bits to the fcntl flags for this fd. Returns the new flags. -export auto setflags(fd &self, int newflags) - -> std::expected<int, std::error_code> -{ - auto flags = getflags(self); - if (!flags) - return flags; - - *flags |= newflags; - auto const ret = replaceflags(self, *flags); - if (!ret) - return std::unexpected(ret.error()); - - return {*flags}; -} - -// Remove bits from the fcntl flags for this fd. Returns the new flags. -export auto clearflags(fd &self, int clrflags) - -> std::expected<int, std::error_code> -{ - auto flags = getflags(self); - if (!flags) - return flags; - - *flags &= ~clrflags; - auto const ret = replaceflags(self, *flags); - if (!ret) - return std::unexpected(ret.error()); - - return {*flags}; -} - -// Return the fd flags for this fd. -export auto getfdflags(fd const &self) -> std::expected<int, std::error_code> -{ - auto const flags = ::fcntl(self.get(), F_GETFD); - if (flags != -1) - return {flags}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Replace the fd flags for this fd. -export auto replacefdflags(fd &self, int newflags) - -> std::expected<void, std::error_code> -{ - auto const ret = ::fcntl(self.get(), F_SETFD, newflags); - if (ret != -1) - return {}; - - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -// Add bits to the fd flags for this fd. Returns the new flags. -export auto setfdflags(fd &self, int newflags) - -> std::expected<int, std::error_code> -{ - auto flags = getfdflags(self); - if (!flags) - return flags; - - *flags |= newflags; - auto const ret = replacefdflags(self, *flags); - if (!ret) - return std::unexpected(ret.error()); - - return {*flags}; -} - -// Remove bits from the fd flags for this fd. Returns the new flags. -export auto clearfdflags(fd &self, int clrflags) - -> std::expected<int, std::error_code> -{ - auto flags = getfdflags(self); - if (!flags) - return flags; - - *flags &= ~clrflags; - auto ret = replacefdflags(self, *flags); - if (!ret) - return std::unexpected(ret.error()); - - return {*flags}; -} - -// Create two fds by calling pipe() and return them. -export auto pipe() -> std::expected<std::pair<fd, fd>, std::error_code> { - auto fds = std::array<int, 2>{}; - - if (auto const ret = ::pipe(fds.data()); ret != 0) - return std::unexpected(std::make_error_code(std::errc(errno))); - - return {{fd(fds[0]), fd(fds[1])}}; -} - -/* - * Write data to a file descriptor from the provided buffer. Returns the - * number of bytes (not objects) written. Incomplete writes may cause a - * partial object to be written. - */ -export auto write(fd &file, std::ranges::contiguous_range auto &&range) - -> std::expected<std::size_t, std::error_code> -{ - auto const ret = ::write(file.get(), std::ranges::data(range), - std::ranges::size(range)); - if (ret >= 0) - return ret; - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -/* - * Read data from a file descriptor into the provided buffer. Returns the - * number of bytes (not objects) read. Incomplete reads may cause a partial - * object to be read. - */ -export auto read(fd &file, std::ranges::contiguous_range auto &&range) - -> std::expected<std::size_t, std::error_code> -{ - auto const ret = ::read(file.get(), std::ranges::data(range), - std::ranges::size(range)); - if (ret >= 0) - return ret; - return std::unexpected(std::make_error_code(std::errc(errno))); -} - -} // namespace nihil |
