MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
ip.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#ifndef MTCORE_IP_HPP
20#define MTCORE_IP_HPP
21
24#include "mtcore/core.hpp"
25#include "mtcore/io/writer.hpp"
26
31
33namespace mtcore {
34
39 enum class IpInitError { INVALID_INPUT };
40
46
52 struct IPv6;
53
59 struct IPv4 {
60 u32 bits = 0;
61
63 [[nodiscard]] Result<void, IpInitError> init(const Slice<char> &str);
64 [[nodiscard]] Result<void, IpInitError> init(const std::string_view &str);
65 [[nodiscard]] Result<void, IpInitError> init(const std::string &str);
66
67 [[nodiscard]] IPv6 to_v6() const noexcept;
68
69 [[nodiscard]] std::strong_ordering operator<=>(const IPv4 &o) const noexcept { return bits <=> o.bits; }
70 [[nodiscard]] bool operator==(const IPv4 &o) const noexcept { return (*this <=> o) == 0; }
71 [[nodiscard]] bool operator!=(const IPv4 &o) const noexcept { return (*this <=> o) != 0; }
72 [[nodiscard]] bool operator<=(const IPv4 &o) const noexcept { return (*this <=> o) <= 0; }
73 [[nodiscard]] bool operator>=(const IPv4 &o) const noexcept { return (*this <=> o) >= 0; }
74 [[nodiscard]] bool operator<(const IPv4 &o) const noexcept { return (*this <=> o) < 0; }
75 [[nodiscard]] bool operator>(const IPv4 &o) const noexcept { return (*this <=> o) > 0; }
76 };
77
83 struct IPv6 {
84 u64 high = 0;
85 u64 low = 0;
86
88 [[nodiscard]] Result<void, IpInitError> init(const Slice<char> &str);
89 [[nodiscard]] Result<void, IpInitError> init(const std::string_view &str);
90 [[nodiscard]] Result<void, IpInitError> init(const std::string &str);
91
92 [[nodiscard]] std::strong_ordering operator<=>(const IPv6 &o) const noexcept {
93 const auto highCmp = high <=> o.high;
94 const auto lowCmp = low <=> o.low;
95 if (highCmp == std::strong_ordering::equal) {
96 return lowCmp;
97 }
98 return highCmp;
99 }
100 [[nodiscard]] bool operator==(const IPv6 &o) const noexcept { return (*this <=> o) == 0; }
101 [[nodiscard]] bool operator!=(const IPv6 &o) const noexcept { return (*this <=> o) != 0; }
102 [[nodiscard]] bool operator<=(const IPv6 &o) const noexcept { return (*this <=> o) <= 0; }
103 [[nodiscard]] bool operator>=(const IPv6 &o) const noexcept { return (*this <=> o) >= 0; }
104 [[nodiscard]] bool operator<(const IPv6 &o) const noexcept { return (*this <=> o) < 0; }
105 [[nodiscard]] bool operator>(const IPv6 &o) const noexcept { return (*this <=> o) > 0; }
106
107 [[nodiscard]] bool is_ip4() const noexcept {
108 return high == 0 && ((low & 0xffffffff00000000ull) == 0x0000ffff00000000ull);
109 }
110
112 };
113
114 struct SubnetMaskV6;
115 struct SubnetV4;
116
124
131 u8 id = 0;
132
138 inline static SubnetMaskV4 by_id(u8 id);
139
144 constexpr static bool valid_id(u8 id);
145
152 };
153
154 namespace impl {
155 constexpr std::array<SubnetMaskV4, 33> init_ip4_masks() {
156 std::array<SubnetMaskV4, 33> res = {};
157 for (size_t i = 1; i < res.size(); ++i) {
158 res[i].id = i;
159 res[i].mask = (res[i - 1].mask >> 1) | 0b10000000000000000000000000000000;
160 }
161 return res;
162 }
163
164 constexpr auto ip4SubnetMasks = init_ip4_masks();
165 } // namespace impl
166
167 constexpr bool SubnetMaskV4::valid_id(const u8 id) { return id < impl::ip4SubnetMasks.size(); }
168
170 ensure(valid_id(id));
171 return impl::ip4SubnetMasks[id];
172 }
173
174 struct SubnetV6;
175
181 u8 id = 0;
184
189 inline static SubnetMaskV6 by_id(u8 id);
190
195 constexpr static bool valid_id(u8 id);
196
203 };
204
205 namespace impl {
206 constexpr std::array<SubnetMaskV6, 129> init_ip6_masks() {
207 std::array<SubnetMaskV6, 129> res = {};
208 for (size_t i = 1; i < res.size(); ++i) {
209 res[i].id = i;
210 if (res[i - 1].maskHigh != std::numeric_limits<u64>::max()) {
211 res[i].maskHigh =
212 (res[i - 1].maskHigh >> 1) | 0b1000000000000000000000000000000000000000000000000000000000000000;
213 }
214 else {
215 res[i].maskHigh = std::numeric_limits<u64>::max();
216 res[i].maskLow =
217 (res[i - 1].maskLow >> 1) | 0b1000000000000000000000000000000000000000000000000000000000000000;
218 }
219 }
220 return res;
221 }
222
223 constexpr auto ip6SubnetMasks = init_ip6_masks();
224 } // namespace impl
225
227 ensure(valid_id(id));
228 return impl::ip6SubnetMasks[id];
229 }
230
231 constexpr bool SubnetMaskV6::valid_id(u8 id) { return id < impl::ip6SubnetMasks.size(); }
232
237 struct SubnetV4 {
238 IPv4 ip = {};
240
244 [[nodiscard]] bool is_valid() const {
246 return false;
247 }
249 }
250
251 bool operator==(const SubnetV4 &o) const { return mask_id == o.mask_id && ip == o.ip; }
252 bool operator!=(const SubnetV4 &o) const { return !(o == *this); }
253 };
254
259 struct SubnetV6 {
260 IPv6 ip = {};
262
266 [[nodiscard]] bool is_valid() const {
268 return false;
269 }
271 }
272
273 bool operator==(const SubnetV6 &o) const { return mask_id == o.mask_id && ip == o.ip; }
274 bool operator!=(const SubnetV6 &o) const { return !(o == *this); }
275 };
276} // namespace mtcore
277
278#include "io/formats.hpp"
279
280namespace mtcore {
286 template<>
288 template<WriterImpl WI>
290 const auto p1 = (val.bits >> 24) & 0xff;
291 const auto p2 = (val.bits >> 16) & 0xff;
292 const auto p3 = (val.bits >> 8) & 0xff;
293 const auto p4 = (val.bits >> 0) & 0xff;
294 return print(writer, "{d}.{d}.{d}.{d}", p1, p2, p3, p4);
295 }
296 };
297
309 template<>
311 template<WriterImpl WI>
313 auto chunks = std::array{
314 (val.high >> 48) & 0xffff, (val.high >> 32) & 0xffff, (val.high >> 16) & 0xffff, (val.high >> 0) & 0xffff,
315 (val.low >> 48) & 0xffff, (val.low >> 32) & 0xffff, (val.low >> 16) & 0xffff, (val.low >> 0) & 0xffff,
316 };
317 auto data = slice_from(chunks);
318
319 auto fmtStr = "{\\::0<4;x}";
320
321 if (slices::contains('z', opts.formatStr)) {
322 fmtStr = "{\\::x}";
323 }
324
325 if (slices::contains('4', opts.formatStr) && val.is_ip4()) {
326 auto prefix = slice_from("0000:0000:0000:0000:0000:ffff:");
327 if (slices::contains('t', opts.formatStr)) {
328 prefix = slice_from("::ffff:");
329 }
330 else if (slices::contains('z', opts.formatStr)) {
331 prefix = slice_from("0:0:0:0:0:ffff:");
332 }
333 size_t written = 0;
334 auto preRes = writer.write_all(prefix);
335 if (preRes.is_error()) {
336 return preRes.error();
337 }
338 written += prefix.size();
339 auto ip4 = val.to_ip4().value();
340 auto ip4Res = format(writer, {}, ip4);
341 if (ip4Res.is_error()) {
342 return ip4Res.error();
343 }
344 written += ip4Res.value();
345 return written;
346 }
347
348 // If there are zeros to truncate
349 if (slices::contains('t', opts.formatStr) && slices::contains(0, data)) {
350 size_t i = 0;
351 for (; i < data.size() && data[i] != 0; ++i) {}
352
353 auto skipStart = slices::first_index(0, data);
354 ensure(skipStart.has_value());
355 auto skipEndOpt = slices::first_index_not(0, data.sub(skipStart.value()));
356 auto skipEnd = skipEndOpt.has_value() ? skipEndOpt.value() + skipStart.value() : data.size();
357
358 auto printPart1 = data.sub(0, skipStart.value());
359 auto printPart2 = data.sub(skipEnd);
360
361 size_t written = 0;
362
363 if (!printPart1.empty()) {
364 auto p1Res = print(writer, fmtStr, printPart1);
365 if (p1Res.is_error()) {
366 return p1Res.error();
367 }
368 written += p1Res.value();
369 }
370
371 auto sepRes = writer.write_all(slice_from("::"));
372 if (sepRes.is_error()) {
373 return sepRes.error();
374 }
375 written += 2;
376
377 if (!printPart2.empty()) {
378 auto p2Res = print(writer, fmtStr, printPart2);
379 if (p2Res.is_error()) {
380 return p2Res.error();
381 }
382 written += p2Res.value();
383 }
384
385 return written;
386 }
387
388 return print(writer, fmtStr, data);
389 }
390 };
391
396 template<>
398 template<WriterImpl WI>
400 const SubnetMaskV4 &val) {
401 return print(writer, "/{d}", val.id);
402 }
403 };
404
409 template<>
411 template<WriterImpl WI>
413 const SubnetMaskV6 &val) {
414 return print(writer, "/{d}", val.id);
415 }
416 };
417
422 template<>
424 template<WriterImpl WI>
426 if (!val.is_valid()) {
427 return print(writer, "[INVALID SUBNET V4]{}{}", val.ip, SubnetMaskV4::by_id(val.mask_id));
428 }
429 return print(writer, "{}{}", val.ip, SubnetMaskV4::by_id(val.mask_id));
430 }
431 };
432
437 template<>
439 template<WriterImpl WI>
441 if (!val.is_valid()) {
442 return print(writer, "[INVALID SUBNET V6]{}{}", val.ip, SubnetMaskV6::by_id(val.mask_id));
443 }
444 return print(writer, "{zt4}{}", val.ip, SubnetMaskV4::by_id(val.mask_id));
445 }
446 };
447} // namespace mtcore
448
449#endif // MTCORE_IP_HPP
constexpr Slice< const char32_t > slice_from(char32_t *cstr)
Creates a slice from a utf32 string in the form of a c string.
mtcore::Optional< size_t > first_index(const Slice< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
mtcore::Optional< size_t > first_index_not(const std::remove_const_t< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
bool contains(const std::remove_const_t< T > &needle, const Slice< T > &haystack)
Checks whether a slice (the haystack) contains an element (the needle) Uses the equality operator of ...
IpInitError
Error when initializing an IP address.
Definition ip.hpp:39
Ip6ConversionError
Error when converting from IPv6 to IPv4.
Definition ip.hpp:45
SubnetMaskError
Error when creating a subnet mask.
Definition ip.hpp:121
Result< size_t, typename Writer< WI >::ErrType > print(Writer< WI > &writer, const char *fmt, const Args &...args)
Prints arguments using a format string Element insert points are designated by a pair of curly braces...
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
uint64_t u64
Alias for 64-bit unsigned ints.
uint8_t u8
Alias for 8-bit unsigned ints.
uint32_t u32
Alias for 32-bit unsigned ints.
Result< size_t, typename Writer< WI >::ErrType > format(Writer< WI > &writer, const FormatOptions &opts, Arg arg)
Generic format function which takes a bunch of arguments (of the same type) and formatting options an...
Core library for C++ with Zig-related functionality.
Represents an IP version 4 address (32 bits) Can be converted to an IPv6.
Definition ip.hpp:59
Result< void, IpInitError > init(const std::string_view &str)
Result< void, IpInitError > init(const Slice< char > &str)
bool operator>=(const IPv4 &o) const noexcept
Definition ip.hpp:73
bool operator<=(const IPv4 &o) const noexcept
Definition ip.hpp:72
bool operator!=(const IPv4 &o) const noexcept
Definition ip.hpp:71
bool operator>(const IPv4 &o) const noexcept
Definition ip.hpp:75
bool operator==(const IPv4 &o) const noexcept
Definition ip.hpp:70
bool operator<(const IPv4 &o) const noexcept
Definition ip.hpp:74
u32 bits
Definition ip.hpp:60
Result< void, IpInitError > init(const Slice< const char > &str)
IPv6 to_v6() const noexcept
Result< void, IpInitError > init(const std::string &str)
Represents an IP version 6 address (128 bits) If it represents an IPv4 address, can be converted to a...
Definition ip.hpp:83
Result< void, IpInitError > init(const std::string &str)
bool operator!=(const IPv6 &o) const noexcept
Definition ip.hpp:101
bool operator<(const IPv6 &o) const noexcept
Definition ip.hpp:104
u64 high
Definition ip.hpp:84
Result< void, IpInitError > init(const Slice< char > &str)
Result< IPv4, Ip6ConversionError > to_ip4() const
bool operator==(const IPv6 &o) const noexcept
Definition ip.hpp:100
bool operator>=(const IPv6 &o) const noexcept
Definition ip.hpp:103
bool is_ip4() const noexcept
Definition ip.hpp:107
u64 low
Definition ip.hpp:85
std::strong_ordering operator<=>(const IPv6 &o) const noexcept
Definition ip.hpp:92
bool operator>(const IPv6 &o) const noexcept
Definition ip.hpp:105
Result< void, IpInitError > init(const std::string_view &str)
bool operator<=(const IPv6 &o) const noexcept
Definition ip.hpp:102
Result< void, IpInitError > init(const Slice< const char > &str)
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Definition result.hpp:170
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
Represents the bitwise mask for a version 4 subnet.
Definition ip.hpp:129
static constexpr bool valid_id(u8 id)
Checks if an id is a valid subnet mask id (0-32)
Definition ip.hpp:167
SubnetV4 subnet_ip(const IPv4 &ip)
Subnets an IP address based on the IP mask.
static SubnetMaskV4 by_id(u8 id)
Retrieves a subnet mask by id (valid ids is 0-32).
Definition ip.hpp:169
Gets a subnet mask for an IP version 6 subnet.
Definition ip.hpp:180
SubnetV6 subnet_ip(const IPv6 &ip)
Subnets an IP address using the subnet mask.
static constexpr bool valid_id(u8 id)
Checks if a subnet mask ID is valid (0-128)
Definition ip.hpp:231
static SubnetMaskV6 by_id(u8 id)
Gets a subnet mask by id.
Definition ip.hpp:226
A masked IP version 4 address.
Definition ip.hpp:237
bool operator!=(const SubnetV4 &o) const
Definition ip.hpp:252
bool operator==(const SubnetV4 &o) const
Definition ip.hpp:251
bool is_valid() const
Checks if a masked IP address is valid (i.e.
Definition ip.hpp:244
A masked IP version 6 address.
Definition ip.hpp:259
bool is_valid() const
Checks if a masked IP address is valid (i.e.
Definition ip.hpp:266
bool operator!=(const SubnetV6 &o) const
Definition ip.hpp:274
bool operator==(const SubnetV6 &o) const
Definition ip.hpp:273
Options for specifying formatting.
Definition format.hpp:36
Slice< const char > formatStr
Definition format.hpp:37
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > &writer, FormatOptions, const IPv4 &val)
Definition ip.hpp:289
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > &writer, FormatOptions opts, const IPv6 &val)
Definition ip.hpp:312
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > &writer, FormatOptions, const SubnetMaskV4 &val)
Definition ip.hpp:399
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > &writer, FormatOptions, const SubnetMaskV6 &val)
Definition ip.hpp:412
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > *writer, FormatOptions, const SubnetV4 &val)
Definition ip.hpp:425
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > *writer, FormatOptions, const SubnetV6 &val)
Definition ip.hpp:440
Struct to override to specify how a type should be formatted.
Definition format.hpp:45
A writer that writes data to some sort of stream or buffer Note: the data elements written should be ...
Definition io/writer.hpp:51