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 <algorithm>
15  
#include <algorithm>
16  
#include <compare>
16  
#include <compare>
17  
#include <cstddef>
17  
#include <cstddef>
18  
#include <cstdint>
18  
#include <cstdint>
19  
#include <cstring>
19  
#include <cstring>
20  
#include <iosfwd>
20  
#include <iosfwd>
21  
#include <string_view>
21  
#include <string_view>
22  
#include <system_error>
22  
#include <system_error>
23  

23  

24  
namespace boost::corosio {
24  
namespace boost::corosio {
25  

25  

26  
/** A Unix domain socket endpoint (filesystem path).
26  
/** A Unix domain socket endpoint (filesystem path).
27  

27  

28  
    Stores the path in a fixed-size buffer, avoiding heap
28  
    Stores the path in a fixed-size buffer, avoiding heap
29  
    allocation. The object is trivially copyable.
29  
    allocation. The object is trivially copyable.
30  

30  

31  
    Abstract sockets (Linux-only) are represented by paths whose
31  
    Abstract sockets (Linux-only) are represented by paths whose
32  
    first character is '\0'. The full path including the leading
32  
    first character is '\0'. The full path including the leading
33  
    null byte is stored.
33  
    null byte is stored.
34  

34  

35  
    The library does NOT automatically unlink the socket path on
35  
    The library does NOT automatically unlink the socket path on
36  
    close — callers are responsible for cleanup.
36  
    close — callers are responsible for cleanup.
37  

37  

38  
    @par Thread Safety
38  
    @par Thread Safety
39  
    Distinct objects: Safe.@n
39  
    Distinct objects: Safe.@n
40  
    Shared objects: Safe.
40  
    Shared objects: Safe.
41  
*/
41  
*/
42  
class BOOST_COROSIO_DECL local_endpoint
42  
class BOOST_COROSIO_DECL local_endpoint
43  
{
43  
{
44  
    // sun_path is 108 on Linux, 104 on macOS/FreeBSD. Use the
44  
    // sun_path is 108 on Linux, 104 on macOS/FreeBSD. Use the
45  
    // minimum so local_endpoint is portable across all three.
45  
    // minimum so local_endpoint is portable across all three.
46  
    char path_[104]{};
46  
    char path_[104]{};
47  
    std::uint8_t len_ = 0;
47  
    std::uint8_t len_ = 0;
48  

48  

49  
public:
49  
public:
50  
    /// Maximum path length for a Unix domain socket (excluding null terminator).
50  
    /// Maximum path length for a Unix domain socket (excluding null terminator).
51  
    static constexpr std::size_t max_path_length = 103;
51  
    static constexpr std::size_t max_path_length = 103;
52  

52  

53  
    /// Default constructor. Creates an empty (unbound) endpoint.
53  
    /// Default constructor. Creates an empty (unbound) endpoint.
54  
    local_endpoint() noexcept = default;
54  
    local_endpoint() noexcept = default;
55  

55  

56  
    /** Construct from a path.
56  
    /** Construct from a path.
57  

57  

58  
        @param path The filesystem path for the socket.
58  
        @param path The filesystem path for the socket.
59  
            Must not exceed @ref max_path_length bytes.
59  
            Must not exceed @ref max_path_length bytes.
60  

60  

61  
        @throws std::system_error if the path is too long.
61  
        @throws std::system_error if the path is too long.
62  
    */
62  
    */
63  
    explicit local_endpoint(std::string_view path);
63  
    explicit local_endpoint(std::string_view path);
64  

64  

65  
    /** Construct from a path (no-throw).
65  
    /** Construct from a path (no-throw).
66  

66  

67  
        @param path The filesystem path for the socket.
67  
        @param path The filesystem path for the socket.
68  
        @param ec Set to an error if the path is too long.
68  
        @param ec Set to an error if the path is too long.
69  
    */
69  
    */
70  
    local_endpoint(std::string_view path, std::error_code& ec) noexcept;
70  
    local_endpoint(std::string_view path, std::error_code& ec) noexcept;
71  

71  

72  
    /** Return the socket path.
72  
    /** Return the socket path.
73  

73  

74  
        For abstract sockets, the returned view includes the
74  
        For abstract sockets, the returned view includes the
75  
        leading null byte.
75  
        leading null byte.
76  

76  

77  
        @return A view over the stored path bytes.
77  
        @return A view over the stored path bytes.
78  
    */
78  
    */
79  
    std::string_view path() const noexcept
79  
    std::string_view path() const noexcept
80  
    {
80  
    {
81  
        return std::string_view(path_, len_);
81  
        return std::string_view(path_, len_);
82  
    }
82  
    }
83  

83  

84  
    /** Check if this is an abstract socket (Linux-only).
84  
    /** Check if this is an abstract socket (Linux-only).
85  

85  

86  
        Abstract sockets live in a kernel namespace rather than
86  
        Abstract sockets live in a kernel namespace rather than
87  
        the filesystem. They are identified by a leading null byte
87  
        the filesystem. They are identified by a leading null byte
88  
        in the path.
88  
        in the path.
89  

89  

90  
        @return `true` if the path starts with '\\0'.
90  
        @return `true` if the path starts with '\\0'.
91  
    */
91  
    */
92  
    bool is_abstract() const noexcept
92  
    bool is_abstract() const noexcept
93  
    {
93  
    {
94  
        return len_ > 0 && path_[0] == '\0';
94  
        return len_ > 0 && path_[0] == '\0';
95  
    }
95  
    }
96  

96  

97  
    /// Return true if the endpoint has no path.
97  
    /// Return true if the endpoint has no path.
98  
    bool empty() const noexcept
98  
    bool empty() const noexcept
99  
    {
99  
    {
100  
        return len_ == 0;
100  
        return len_ == 0;
101  
    }
101  
    }
102  

102  

103  
    /// Compare endpoints for equality.
103  
    /// Compare endpoints for equality.
104  
    friend bool
104  
    friend bool
105  
    operator==(local_endpoint const& a, local_endpoint const& b) noexcept
105  
    operator==(local_endpoint const& a, local_endpoint const& b) noexcept
106  
    {
106  
    {
107  
        return a.len_ == b.len_ &&
107  
        return a.len_ == b.len_ &&
108  
            std::memcmp(a.path_, b.path_, a.len_) == 0;
108  
            std::memcmp(a.path_, b.path_, a.len_) == 0;
109  
    }
109  
    }
110  

110  

111  
    /** Format the endpoint for stream output.
111  
    /** Format the endpoint for stream output.
112  

112  

113  
        Non-abstract paths are printed as-is. Abstract paths
113  
        Non-abstract paths are printed as-is. Abstract paths
114  
        (leading null byte) are printed as `[abstract:name]`.
114  
        (leading null byte) are printed as `[abstract:name]`.
115  
        Empty endpoints produce no output.
115  
        Empty endpoints produce no output.
116  

116  

117  
        @param os The output stream.
117  
        @param os The output stream.
118  
        @param ep The endpoint to format.
118  
        @param ep The endpoint to format.
119  

119  

120  
        @return A reference to @p os.
120  
        @return A reference to @p os.
121  
    */
121  
    */
122  
    friend BOOST_COROSIO_DECL std::ostream&
122  
    friend BOOST_COROSIO_DECL std::ostream&
123  
    operator<<(std::ostream& os, local_endpoint const& ep);
123  
    operator<<(std::ostream& os, local_endpoint const& ep);
124  

124  

125  
    /// Lexicographic ordering on stored path bytes.
125  
    /// Lexicographic ordering on stored path bytes.
126  
    friend std::strong_ordering
126  
    friend std::strong_ordering
127  
    operator<=>(local_endpoint const& a, local_endpoint const& b) noexcept
127  
    operator<=>(local_endpoint const& a, local_endpoint const& b) noexcept
128  
    {
128  
    {
129  
        auto common = (std::min)(a.len_, b.len_);
129  
        auto common = (std::min)(a.len_, b.len_);
130  
        if (int cmp = std::memcmp(a.path_, b.path_, common); cmp != 0)
130  
        if (int cmp = std::memcmp(a.path_, b.path_, common); cmp != 0)
131  
            return cmp <=> 0;
131  
            return cmp <=> 0;
132  
        return a.len_ <=> b.len_;
132  
        return a.len_ <=> b.len_;
133  
    }
133  
    }
134  
};
134  
};
135  

135  

136  
} // namespace boost::corosio
136  
} // namespace boost::corosio
137  

137  

138  
#endif // BOOST_COROSIO_LOCAL_ENDPOINT_HPP
138  
#endif // BOOST_COROSIO_LOCAL_ENDPOINT_HPP