libs/corosio/src/corosio/src/ipv4_address.cpp

99.0% Lines (101/102) 100.0% Functions (15/15) 88.1% Branches (52/59)
libs/corosio/src/corosio/src/ipv4_address.cpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2026 Vinnie Falco (vinnie dot falco at gmail dot com)
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 #include <boost/corosio/ipv4_address.hpp>
11
12 #include <ostream>
13 #include <stdexcept>
14
15 namespace boost::corosio {
16
17 5029 ipv4_address::ipv4_address(uint_type u) noexcept
18 5029 : addr_(u)
19 {
20 5029 }
21
22 14832 ipv4_address::ipv4_address(bytes_type const& bytes) noexcept
23 {
24 14832 addr_ =
25 14832 (static_cast<std::uint32_t>(bytes[0]) << 24) |
26 14832 (static_cast<std::uint32_t>(bytes[1]) << 16) |
27 14832 (static_cast<std::uint32_t>(bytes[2]) << 8) |
28 14832 (static_cast<std::uint32_t>(bytes[3]));
29 14832 }
30
31 4 ipv4_address::ipv4_address(std::string_view s)
32 {
33 4 auto ec = parse_ipv4_address(s, *this);
34
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
4 if (ec)
35
1/1
✓ Branch 2 taken 3 times.
3 throw std::invalid_argument("invalid IPv4 address");
36 1 }
37
38 auto
39 5026 ipv4_address::to_bytes() const noexcept -> bytes_type
40 {
41 bytes_type bytes;
42 5026 bytes[0] = static_cast<unsigned char>((addr_ >> 24) & 0xff);
43 5026 bytes[1] = static_cast<unsigned char>((addr_ >> 16) & 0xff);
44 5026 bytes[2] = static_cast<unsigned char>((addr_ >> 8) & 0xff);
45 5026 bytes[3] = static_cast<unsigned char>( addr_ & 0xff);
46 5026 return bytes;
47 }
48
49 auto
50 10 ipv4_address::to_uint() const noexcept -> uint_type
51 {
52 10 return addr_;
53 }
54
55 std::string
56 11 ipv4_address::to_string() const
57 {
58 char buf[max_str_len];
59 11 auto n = print_impl(buf);
60
1/1
✓ Branch 1 taken 11 times.
22 return std::string(buf, n);
61 }
62
63 std::string_view
64 4 ipv4_address::to_buffer(char* dest, std::size_t dest_size) const
65 {
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (dest_size < max_str_len)
67 throw std::length_error("buffer too small for IPv4 address");
68 4 auto n = print_impl(dest);
69 4 return std::string_view(dest, n);
70 }
71
72 bool
73 5 ipv4_address::is_loopback() const noexcept
74 {
75 5 return (addr_ & 0xFF000000) == 0x7F000000;
76 }
77
78 bool
79 4 ipv4_address::is_unspecified() const noexcept
80 {
81 4 return addr_ == 0;
82 }
83
84 bool
85 4 ipv4_address::is_multicast() const noexcept
86 {
87 4 return (addr_ & 0xF0000000) == 0xE0000000;
88 }
89
90 std::ostream&
91 1 operator<<(std::ostream& os, ipv4_address const& addr)
92 {
93 char buf[ipv4_address::max_str_len];
94
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
1 os << addr.to_buffer(buf, sizeof(buf));
95 1 return os;
96 }
97
98 std::size_t
99 15 ipv4_address::print_impl(char* dest) const noexcept
100 {
101 15 auto const start = dest;
102 60 auto const write = [](char*& dest, unsigned char v)
103 {
104
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 41 times.
60 if (v >= 100)
105 {
106 19 *dest++ = '0' + v / 100;
107 19 v %= 100;
108 19 *dest++ = '0' + v / 10;
109 19 v %= 10;
110 }
111
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 36 times.
41 else if (v >= 10)
112 {
113 5 *dest++ = '0' + v / 10;
114 5 v %= 10;
115 }
116 60 *dest++ = '0' + v;
117 60 };
118 15 write(dest, static_cast<unsigned char>((addr_ >> 24) & 0xff));
119 15 *dest++ = '.';
120 15 write(dest, static_cast<unsigned char>((addr_ >> 16) & 0xff));
121 15 *dest++ = '.';
122 15 write(dest, static_cast<unsigned char>((addr_ >> 8) & 0xff));
123 15 *dest++ = '.';
124 15 write(dest, static_cast<unsigned char>( addr_ & 0xff));
125 15 return static_cast<std::size_t>(dest - start);
126 }
127
128 //------------------------------------------------
129
130 namespace {
131
132 // Parse a decimal octet (0-255), no leading zeros except "0"
133 // Returns true on success, advances `it`
134 bool
135 193 parse_dec_octet(
136 char const*& it,
137 char const* end,
138 unsigned char& octet) noexcept
139 {
140
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 191 times.
193 if (it == end)
141 2 return false;
142
143 191 char c = *it;
144
4/4
✓ Branch 0 taken 187 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 185 times.
191 if (c < '0' || c > '9')
145 6 return false;
146
147 185 unsigned v = static_cast<unsigned>(c - '0');
148 185 ++it;
149
150
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 156 times.
185 if (v == 0)
151 {
152 // "0" is valid, but "00", "01", etc. are not
153
5/6
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 25 times.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
29 if (it != end && *it >= '0' && *it <= '9')
154 3 return false;
155 26 octet = 0;
156 26 return true;
157 }
158
159 // First digit was 1-9, can have more
160
6/6
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 43 times.
✓ Branch 3 taken 77 times.
✓ Branch 4 taken 42 times.
✓ Branch 5 taken 1 time.
156 if (it != end && *it >= '0' && *it <= '9')
161 {
162 42 v = v * 10 + static_cast<unsigned>(*it - '0');
163 42 ++it;
164
165
4/6
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 39 times.
✗ Branch 5 not taken.
42 if (it != end && *it >= '0' && *it <= '9')
166 {
167 39 v = v * 10 + static_cast<unsigned>(*it - '0');
168 39 ++it;
169
170 // Can't have more than 3 digits
171
5/6
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 36 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
39 if (it != end && *it >= '0' && *it <= '9')
172 1 return false;
173 }
174 }
175
176
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 148 times.
155 if (v > 255)
177 7 return false;
178
179 148 octet = static_cast<unsigned char>(v);
180 148 return true;
181 }
182
183 } // namespace
184
185 std::error_code
186 62 parse_ipv4_address(
187 std::string_view s,
188 ipv4_address& addr) noexcept
189 {
190 62 auto it = s.data();
191 62 auto const end = it + s.size();
192
193 unsigned char octets[4];
194
195 // Parse first octet
196
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 49 times.
62 if (!parse_dec_octet(it, end, octets[0]))
197 13 return std::make_error_code(std::errc::invalid_argument);
198
199 // Parse remaining octets
200
2/2
✓ Branch 0 taken 137 times.
✓ Branch 1 taken 37 times.
174 for (int i = 1; i < 4; ++i)
201 {
202
3/4
✓ Branch 0 taken 131 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 131 times.
137 if (it == end || *it != '.')
203 6 return std::make_error_code(std::errc::invalid_argument);
204 131 ++it; // skip '.'
205
206
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 125 times.
131 if (!parse_dec_octet(it, end, octets[i]))
207 6 return std::make_error_code(std::errc::invalid_argument);
208 }
209
210 // Must have consumed entire string
211
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 32 times.
37 if (it != end)
212 5 return std::make_error_code(std::errc::invalid_argument);
213
214 64 addr = ipv4_address(ipv4_address::bytes_type{{
215 32 octets[0], octets[1], octets[2], octets[3]}});
216
217 32 return {};
218 }
219
220 } // namespace boost::corosio
221