diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-28 19:25:55 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-28 19:25:55 +0100 |
| commit | a2d7181700ac64b8e7a4472ec26dfa253b38f188 (patch) | |
| tree | 23c5a9c8ec4089ac346e2e0f9391909c3089b66b /nihil.posix/fd.ccm | |
| parent | f226d46ee02b57dd76a4793593aa8d66e1c58353 (diff) | |
| download | nihil-a2d7181700ac64b8e7a4472ec26dfa253b38f188.tar.gz nihil-a2d7181700ac64b8e7a4472ec26dfa253b38f188.tar.bz2 | |
split nihil into separate modules
Diffstat (limited to 'nihil.posix/fd.ccm')
| -rw-r--r-- | nihil.posix/fd.ccm | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/nihil.posix/fd.ccm b/nihil.posix/fd.ccm new file mode 100644 index 0000000..b937f46 --- /dev/null +++ b/nihil.posix/fd.ccm @@ -0,0 +1,157 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <coroutine> +#include <expected> +#include <ranges> +#include <span> +#include <stdexcept> +#include <system_error> + +export module nihil.posix:fd; + +import nihil.error; +import nihil.monad; + +namespace nihil { + +/* + * fd: a file descriptor. + */ + +export struct fd final { + // Construct an empty (invalid) fd. + fd() noexcept; + + // Construct an fd from an exising file destrictor, taking ownership. + fd(int fd_) noexcept; + + // Destructor. Close the fd, discarding any errors. + ~fd(); + + // Move from another fd, leaving the moved-from fd in an invalid state. + fd(fd &&other) noexcept; + auto operator=(this fd &, fd &&other) noexcept -> fd &; + + // Not copyable. + fd(fd const &) = delete; + fd& operator=(this fd &, fd const &) = delete; + + // Return true if this fd is valid (open). + [[nodiscard]] explicit operator bool(this fd const &self) noexcept; + + // Close the wrapped fd. + [[nodiscard]] auto close(this fd &self) -> std::expected<void, error>; + + // Return the stored fd. + [[nodiscard]] auto get(this fd const &self) -> int; + + // Release the stored fd and return it. The caller must close it. + [[nodiscard]] auto release(this fd &&self) -> int; + + // Write data from the provided buffer to the fd. Returns the + // number of bytes written. + [[nodiscard]] auto write(this fd &self, std::span<std::byte const>) + -> std::expected<std::size_t, error>; + + // Read data from the fd to the provided buffer. Returns a + // subspan containing the data which was read. + [[nodiscard]] auto read(this fd &self, std::span<std::byte>) + -> std::expected<std::span<std::byte>, error>; + +private: + static constexpr int invalid_fileno = -1; + + int m_fileno = invalid_fileno; +}; + +// Create a copy of this fd by calling dup(). +export [[nodiscard]] auto dup(fd const &self) -> std::expected<fd, error>; + +// 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 [[nodiscard]] auto dup(fd const &self, int newfd) + -> std::expected<fd, error>; + +// Create a copy of this fd by calling dup(). +export [[nodiscard]] auto raw_dup(fd const &self) + -> std::expected<int, error>; + +// Create a copy of this fd by calling dup2(). +export [[nodiscard]] auto raw_dup(fd const &self, int newfd) + -> std::expected<int, error>; + +// Return the fnctl flags for this fd. +export [[nodiscard]] auto getflags(fd const &self) + -> std::expected<int, error>; + +// Replace the fnctl flags for this fd. +export [[nodiscard]] auto replaceflags(fd &self, int newflags) + -> std::expected<void, error>; + +// Add bits to the fcntl flags for this fd. Returns the new flags. +export [[nodiscard]] auto setflags(fd &self, int newflags) + -> std::expected<int, error>; + +// Remove bits from the fcntl flags for this fd. Returns the new flags. +export [[nodiscard]] auto clearflags(fd &self, int clrflags) + -> std::expected<int, error>; + +// Return the fd flags for this fd. +export [[nodiscard]] auto getfdflags(fd const &self) + -> std::expected<int, error>; + +// Replace the fd flags for this fd. +export [[nodiscard]] auto replacefdflags(fd &self, int newflags) + -> std::expected<void, error>; + +// Add bits to the fd flags for this fd. Returns the new flags. +export [[nodiscard]] auto setfdflags(fd &self, int newflags) + -> std::expected<int, error>; + +// Remove bits from the fd flags for this fd. Returns the new flags. +export [[nodiscard]] auto clearfdflags(fd &self, int clrflags) + -> std::expected<int, error>; + +// Create two fds by calling pipe() and return them. +export [[nodiscard]] auto pipe() -> std::expected<std::pair<fd, fd>, error>; + +/* + * Write data to a file descriptor from the provided range. Returns the + * number of bytes written. + */ +export [[nodiscard]] auto write(fd &file, + std::ranges::contiguous_range auto &&range) + -> std::expected<std::size_t, error> +requires(sizeof(std::ranges::range_value_t<decltype(range)>) == 1) +{ + return file.write(as_bytes(std::span(range))); +} + +/* + * Read data from a file descriptor into the provided buffer. Returns a + * span containing the data that was read. + */ +export [[nodiscard]] auto read(fd &file, + std::ranges::contiguous_range auto &&range) + -> std::expected< + std::span<std::ranges::range_value_t<decltype(range)>>, + error> +requires(sizeof(std::ranges::range_value_t<decltype(range)>) == 1) +{ + auto bspan = as_writable_bytes(std::span(range)); + auto rspan = co_await file.read(bspan); + co_return std::span(range).subspan(0, rspan.size()); +} + +} // namespace nihil |
