include/boost/corosio/local_endpoint.hpp

100.0% Lines (14 / 14) 94.4% Branches (17 / 18) 100.0% Functions (6 / 6)
local_endpoint.hpp
f(x) Functions (6)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #ifndef BOOST_COROSIO_LOCAL_ENDPOINT_HPP
11 #define BOOST_COROSIO_LOCAL_ENDPOINT_HPP
12
13 #include <boost/corosio/detail/config.hpp>
14
15 #include <boost/capy/results.hpp>
16
17 #include <algorithm>
18 #include <compare>
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <iosfwd>
23 #include <string_view>
24 #include <system_error>
25
26 namespace boost::corosio {
27
28 /** Holds the filesystem path that names a Unix domain socket.
29
30 Stores the path in a fixed-size buffer, avoiding heap
31 allocation. The object is trivially copyable.
32
33 Abstract sockets (Linux-only) are represented by paths whose
34 first character is '\0'. The full path including the leading
35 null byte is stored.
36
37 The library does NOT automatically unlink the socket path on
38 close. Callers are responsible for cleanup.
39
40 @par Thread Safety
41 Distinct objects: Safe.@n
42 Shared objects: Safe.
43
44 @see @ref boost::corosio::make_local_endpoint
45 */
46 class BOOST_COROSIO_DECL local_endpoint
47 {
48 struct from_path_t
49 {};
50
51 // Callers have already checked the length against max_path_length.
52 local_endpoint(from_path_t, std::string_view path) noexcept;
53
54 friend BOOST_COROSIO_DECL capy::results<local_endpoint>
55 make_local_endpoint(std::string_view path) noexcept;
56
57 // sun_path is 108 on Linux, 104 on macOS/FreeBSD. Use the
58 // minimum so local_endpoint is portable across all three.
59 char path_[104]{};
60 std::uint8_t len_ = 0;
61
62 public:
63 /// Maximum path length for a Unix domain socket (excluding null terminator).
64 static constexpr std::size_t max_path_length = 103;
65
66 /// Default constructor. Creates an empty (unbound) endpoint.
67 2729x local_endpoint() noexcept = default;
68
69 /** Return the socket path.
70
71 For abstract sockets, the returned view includes the
72 leading null byte.
73
74 @return A view over the stored path bytes.
75 */
76 94x std::string_view path() const noexcept
77 {
78 94x return std::string_view(path_, len_);
79 }
80
81 /** Check if this is an abstract socket (Linux-only).
82
83 Abstract sockets live in a kernel namespace rather than
84 the filesystem. They are identified by a leading null byte
85 in the path.
86
87 @return `true` if the path starts with '\\0'.
88 */
89 137x bool is_abstract() const noexcept
90 {
91
4/4
✓ Branch 2 → 3 taken 136 times.
✓ Branch 2 → 5 taken 1 time.
✓ Branch 3 → 4 taken 7 times.
✓ Branch 3 → 5 taken 129 times.
137x return len_ > 0 && path_[0] == '\0';
92 }
93
94 /// Return true if the endpoint has no path.
95 47x bool empty() const noexcept
96 {
97 47x return len_ == 0;
98 }
99
100 /// Compare endpoints for equality.
101 friend bool
102 42x operator==(local_endpoint const& a, local_endpoint const& b) noexcept
103 {
104
4/4
✓ Branch 2 → 3 taken 40 times.
✓ Branch 2 → 5 taken 2 times.
✓ Branch 3 → 4 taken 38 times.
✓ Branch 3 → 5 taken 2 times.
42x return a.len_ == b.len_ && std::memcmp(a.path_, b.path_, a.len_) == 0;
105 }
106
107 /** Format the endpoint for stream output.
108
109 Non-abstract paths are printed as-is. Abstract paths
110 (leading null byte) are printed as `[abstract:name]`.
111 Empty endpoints produce no output.
112
113 @param os The output stream.
114 @param ep The endpoint to format.
115
116 @return A reference to @p os.
117 */
118 friend BOOST_COROSIO_DECL std::ostream&
119 operator<<(std::ostream& os, local_endpoint const& ep);
120
121 /// Lexicographic ordering on stored path bytes.
122 friend std::strong_ordering
123 35x operator<=>(local_endpoint const& a, local_endpoint const& b) noexcept
124 {
125 35x auto common = (std::min)(a.len_, b.len_);
126
2/2
✓ Branch 3 → 4 taken 16 times.
✓ Branch 3 → 10 taken 19 times.
35x if (int cmp = std::memcmp(a.path_, b.path_, common); cmp != 0)
127
3/4
✓ Branch 4 → 5 taken 16 times.
✗ Branch 4 → 8 not taken.
✓ Branch 5 → 6 taken 11 times.
✓ Branch 5 → 7 taken 5 times.
16x return cmp <=> 0;
128
4/4
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 14 taken 12 times.
✓ Branch 11 → 12 taken 5 times.
✓ Branch 11 → 13 taken 2 times.
19x return a.len_ <=> b.len_;
129 }
130 };
131
132 /** Make a local endpoint from a filesystem path.
133
134 A path longer than @ref local_endpoint::max_path_length cannot be
135 represented in a socket address. Such a path reports
136 `errc::filename_too_long` with an empty endpoint.
137
138 @par Example
139 @par !example make_local_endpoint
140
141 @param path The socket path; an empty path yields an unbound endpoint.
142
143 @return The error code and the endpoint.
144 */
145 [[nodiscard]] BOOST_COROSIO_DECL capy::results<local_endpoint>
146 make_local_endpoint(std::string_view path) noexcept;
147
148 } // namespace boost::corosio
149
150 #endif // BOOST_COROSIO_LOCAL_ENDPOINT_HPP
151