aboutsummaryrefslogtreecommitdiffstats
path: root/modules/fd.ccm
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-21 12:27:20 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-21 12:27:20 +0100
commit243d958df14b85788232aca623b83826115a5eb9 (patch)
treed1f698296b053359a5563731cda8c51df9ab9a6c /modules/fd.ccm
parent8a36eb498e1a1c2cf2e886356faa4ce67e52e874 (diff)
downloadnihil-243d958df14b85788232aca623b83826115a5eb9.tar.gz
nihil-243d958df14b85788232aca623b83826115a5eb9.tar.bz2
rename modules/ to nihil/
Diffstat (limited to 'modules/fd.ccm')
-rw-r--r--modules/fd.ccm309
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