aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.posix/posix.fd.ccm
blob: b937f46661b7bb8892e09acd908bce6952ca0d75 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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