ipaddress 1.1.0
Loading...
Searching...
No Matches
base-v4.hpp
Go to the documentation of this file.
1/**
2 * @file base-v4.hpp
3 * @brief Adds basic functionality for working with IPv4
4 * @author Vladimir Shaleev
5 * @copyright MIT License
6 *
7 * This file provides foundational classes and functions necessary for handling IPv4 addresses,
8 * including parsing, constructing, and manipulating IPv4 address representations. It serves as
9 * a base for higher-level IPv4 address operations and utilities.
10 */
11
12#ifndef IPADDRESS_BASE_V4_HPP
13#define IPADDRESS_BASE_V4_HPP
14
16#include "hash.hpp"
17
19
20/**
21 * A template class providing the base functionality for IPv4 addresses.
22 *
23 * This class encapsulates the basic properties and operations for IPv4 addresses,
24 * such as version identification, size, and conversion from string representations.
25 * It is intended to be extended by more specialized IPv4 address classes.
26 *
27 * @tparam Ext The extension type that provides additional functionality.
28 */
29IPADDRESS_EXPORT template <typename Ext>
30class base_v4 {
31public:
32 using base_type = byte_array<4>;
33 using uint_type = uint32_t;
34
35 static constexpr ip_version base_version = ip_version::V4;
36 static constexpr size_t base_size = 4;
37 static constexpr size_t base_max_string_len = 15;
38 static constexpr size_t base_max_prefixlen = base_size * 8;
39 static constexpr uint_type base_all_ones = (uint_type) (-1); // std::numeric_limits<uint_type>::max() may not work due to Windows macros
40
41 /**
42 * Retrieves the IP version of the address.
43 *
44 * @return The IP version enumeration value for IPv4.
45 */
47 return base_version;
48 }
49
50 /**
51 * Retrieves the size of the IPv4 address.
52 *
53 * @return The size of the IPv4 address in bytes.
54 */
56 return base_size;
57 }
58
59protected:
60 template <typename Iter>
61 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base<Ext> ip_from_string(Iter begin, Iter end, error_code& code, uint32_t& index) IPADDRESS_NOEXCEPT {
62 if (begin == end) {
64 return {};
65 }
66
67 base_type octets = {};
68 char first_symbol = '\0';
69 int digits = 0;
70 int octet = 0;
71
72 index = 0;
74 Iter it = begin;
75 uint32_t error_symbol = 0;
76
77 while (it < end) {
78 auto c = internal::next_char_or_error(it, end, code, error_symbol);
79 if (code != error_code::no_error) {
80 index = error_symbol;
81 return {};
82 }
83 if (index >= 4) {
85 return {};
86 }
87 if (c >= '0' && c <= '9') {
88 if (digits > 0 && first_symbol == '0') {
90 return {};
91 } else if (digits == 0) {
92 first_symbol = c;
93 }
94 auto d = c - '0';
95 octet = octet * 10 + d;
96 ++digits;
97 if (digits > 3) {
99 return {};
100 }
101 } else if (c == '.' && digits > 0) {
102 if (octet > 255) {
104 return {};
105 }
106 octets[index++] = uint8_t(octet & 0xFF);
107 digits = 0;
108 octet = 0;
109 } else if (c == '.') {
111 return {};
112 } else {
114 return {};
115 }
116 }
117 if (index != 3) {
119 return {};
120 }
121 if (digits == 0) {
123 return {};
124 }
125 if (octet > 255) {
127 return {};
128 }
129 octets[index] = uint8_t(octet & 0xFF);
130 return ip_address_base<Ext>(octets);
131 }
132
133 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base<Ext> ip_from_uint32(uint_type ip) IPADDRESS_NOEXCEPT {
134 ip = is_little_endian() ? swap_bytes(ip) : ip;
135 return ip_address_base<Ext>({
136 uint8_t(ip & 0xFF),
137 uint8_t((ip >> 8) & 0xFF),
138 uint8_t((ip >> 16) & 0xFF),
139 uint8_t((ip >> 24) & 0xFF) });
140 }
141
142 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base<Ext> ip_from_prefix(size_t prefixlen) {
143 return prefixlen != 0
144 ? ip_address_base<Ext>::ip_from_uint32(base_all_ones ^ (base_all_ones >> (prefixlen - 1) >> 1)) // NOLINT
145 : ip_address_base<Ext>::ip_from_uint32(0);
146 }
147
148 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint_type ip_to_uint32(const base_type& bytes) IPADDRESS_NOEXCEPT {
149 const auto ip =
150 (uint32_t(bytes[3]) << 24) |
151 (uint32_t(bytes[2]) << 16) |
152 (uint32_t(bytes[1]) << 8) |
153 (uint32_t(bytes[0]));
154 return is_little_endian() ? swap_bytes(ip) : ip;
155 }
156
157 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t ip_to_chars(const base_type& bytes, format /*fmt*/, char (&result)[base_max_string_len + 1]) IPADDRESS_NOEXCEPT {
158 size_t offset = 0;
159 char buffer[4] {};
160 for (size_t b = 0; b < 4; ++b) {
161 const size_t length = byte_to_string(bytes[b], buffer);
162 for (size_t i = 0; i < length; ++i) {
163 result[offset++] = buffer[i];
164 }
165 if (b < 3) {
166 result[offset++] = '.';
167 }
168 }
169 result[offset] = '\0';
170 return offset;
171 }
172
173 IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE std::string ip_reverse_pointer(const base_type& bytes) {
174 return ip_from_uint32(swap_bytes(ip_to_uint32(bytes))).to_string() + ".in-addr.arpa";
175 }
176
177 template <typename Iter>
178 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::tuple<ip_address_base<Ext>, size_t> parse_netmask(Iter begin, Iter end, error_code& code, uint32_t& code_value) IPADDRESS_NOEXCEPT {
180 code_value = 0;
181 Iter it = begin;
182
183 size_t prefixlen = 0;
184 auto is_value = true;
185 auto has_prefixlen = false;
186
187 while (it < end) {
188 has_prefixlen = true;
189 const auto c = internal::next_char_or_error(it, end, code, code_value);
190 if (code != error_code::no_error) {
191 return std::make_tuple(ip_address_base<Ext>(), 0);
192 }
193 if (c >= '0' && c <= '9') {
194 prefixlen = prefixlen * 10 + (c - '0');
195 } else {
196 is_value = false;
197 break;
198 }
199 }
200 if (is_value) {
201 if (prefixlen > base_max_prefixlen) {
203 return std::make_tuple(ip_address_base<Ext>(), 0);
204 }
205 } else {
206 auto ip = ip_to_uint32(ip_from_string(begin, end, code, code_value).bytes());
207 if (code != error_code::no_error) {
209 return std::make_tuple(ip_address_base<Ext>(), 0);
210 }
211
212 prefixlen = prefix_from_ip_uint32(ip, code);
213 if (code != error_code::no_error) {
214 ip = ip ^ base_all_ones;
216 prefixlen = prefix_from_ip_uint32(ip, code);
217 if (code != error_code::no_error) {
218 return std::make_tuple(ip_address_base<Ext>(), 0);
219 }
220 }
221 }
222 prefixlen = has_prefixlen ? prefixlen : base_max_prefixlen;
223 auto netmask = ip_from_prefix(prefixlen);
224 return std::make_tuple(netmask, prefixlen);
225 }
226
227 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base<Ext> strict_netmask(const ip_address_base<Ext>& address, const ip_address_base<Ext>& netmask, bool strict, error_code& code) IPADDRESS_NOEXCEPT {
228 auto pack_address = address.to_uint();
229 auto pack_netmask = netmask.to_uint();
230 if ((pack_address & pack_netmask) != pack_address) {
231 if (strict) {
233 return ip_address_base<Ext>();
234 } else {
235 return ip_from_uint32(pack_address & pack_netmask);
236 }
237 }
238 return address;
239 }
240
241private:
242 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t prefix_from_ip_uint32(uint_type ip, error_code& code) IPADDRESS_NOEXCEPT {
243 auto trailing_zeroes = count_righthand_zero_bits(ip, base_max_prefixlen);
244 auto prefixlen = base_max_prefixlen - trailing_zeroes;
245 auto leading_ones = trailing_zeroes != 32 ? (ip >> trailing_zeroes) : 0;
246 auto all_ones = (uint_type(1) << (prefixlen - 1) << 1) - uint_type(1); // NOLINT
247 if (leading_ones != all_ones) {
249 return 0;
250 }
251 return prefixlen;
252 }
253
254 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t count_righthand_zero_bits(uint32_t number, uint32_t bits) IPADDRESS_NOEXCEPT {
255 if (number == 0) {
256 return bits;
257 } else {
258 number = (~number & (number - 1));
259 size_t count = 0;
260 while (number != 0) {
261 count += number & 0x1;
262 number >>= 1;
263 }
264 if (bits < count) {
265 return bits;
266 } else {
267 return count;
268 }
269 }
270 }
271
272 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t byte_to_string(uint8_t byte, char (&str)[4]) IPADDRESS_NOEXCEPT {
273 size_t length = 0;
274 char* p = str;
275 do {
276 *p++ = char((byte % 10) + '0');
277 byte /= 10;
278 ++length;
279 } while (byte != 0);
280
281 *p = '\0';
282
283 for (size_t i = 0, midle = length / 2; i < midle; ++i) {
284 auto tmp = str[i];
285 str[i] = *--p;
286 *p = tmp;
287 }
288
289 return length;
290 }
291};
292
293template <typename Ext>
294constexpr ip_version base_v4<Ext>::base_version;
295
296template <typename Ext>
297constexpr size_t base_v4<Ext>::base_size;
298
299template <typename Ext>
300constexpr size_t base_v4<Ext>::base_max_string_len;
301
302template <typename Ext>
303constexpr size_t base_v4<Ext>::base_max_prefixlen;
304
305template <typename Ext>
306constexpr typename base_v4<Ext>::uint_type base_v4<Ext>::base_all_ones;
307
308} // namespace IPADDRESS_NAMESPACE
309
310#endif // IPADDRESS_BASE_V4_HPP
A template class providing the base functionality for IPv4 addresses.
Definition base-v4.hpp:30
constexpr inline ip_version version() const noexcept
Retrieves the IP version of the address.
Definition base-v4.hpp:46
constexpr inline size_t size() const noexcept
Retrieves the size of the IPv4 address.
Definition base-v4.hpp:55
A template base class for IP address representations.
Definition ip-address-base.hpp:56
#define IPADDRESS_EXPORT
Definition config.hpp:42
#define IPADDRESS_NODISCARD
Definition config.hpp:98
#define IPADDRESS_FORCE_INLINE
Definition config.hpp:112
#define IPADDRESS_NAMESPACE
Definition config.hpp:38
#define IPADDRESS_NOEXCEPT
Definition config.hpp:89
ip_version
Enumerates the IP address versions.
Definition ip-address-base.hpp:29
@ V4
IPv4 version identifier.
constexpr inline bool is_little_endian() noexcept
Checks if the system is little-endian.
Definition endian.hpp:110
error_code
Enumeration of error codes for IP address parsing and validation.
Definition errors.hpp:52
@ octet_has_invalid_symbol
An octet contains characters other than digits, which are invalid.
@ empty_address
The IP address string is empty when it should contain a valid address.
@ octet_exceeded_255
An octet's value exceeds the maximum allowed value of 255.
@ leading_0_are_not_permitted
Leading zeroes are not permitted in any octet of the IPv4 address.
@ empty_octet
An octet in the IPv4 address is empty when it should contain a numeric value.
@ no_error
Indicates the absence of any errors.
@ has_host_bits_set
The address has host bits set when they are expected to be clear.
@ octet_more_3_characters
An octet contains more than three characters, exceeding the maximum allowed.
@ expected_4_octets
The IPv4 address does not contain the expected four octets.
@ netmask_pattern_mixes_zeroes_and_ones
The netmask contains an invalid pattern of zeroes and ones.
@ invalid_netmask
The provided netmask is not valid according to standard netmask formatting rules.
constexpr inline uint32_t swap_bytes(uint32_t value) noexcept
Swaps the bytes of a 32-bit unsigned integer.
Definition endian.hpp:126
A template class for creating and managing a fixed-size array of bytes.
Definition byte-array.hpp:33