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