aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.posix/fd.ccm
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-29 19:19:23 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-29 19:19:23 +0100
commita8b0ea58e60bb0326b7f7c8f3c736d89ce9ef1df (patch)
tree6dafcf2674780649dcdc2649855722357837a68e /nihil.posix/fd.ccm
parent4fa6821e0645ff61a9380cd090abff472205c630 (diff)
downloadnihil-a8b0ea58e60bb0326b7f7c8f3c736d89ce9ef1df.tar.gz
nihil-a8b0ea58e60bb0326b7f7c8f3c736d89ce9ef1df.tar.bz2
wip macOS port
Diffstat (limited to 'nihil.posix/fd.ccm')
-rw-r--r--nihil.posix/fd.ccm157
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