28inline constexpr auto low_mask64(std::size_t len)
noexcept -> uint64_t {
29 return len == 64 ? 0xFFFF'FFFF'FFFF'FFFFull : ((uint64_t{1} << len) - 1u);
33inline constexpr auto low_mask32(std::size_t len)
noexcept -> uint32_t {
34 return len == 32 ? 0xFFFF'FFFFu : ((uint32_t{1} << len) - 1u);
38inline constexpr auto range_mask64(std::size_t begin, std::size_t end)
noexcept
47inline auto classify_ipv6_avx2(__m256i chunk, uint32_t text_mask,
49 uint32_t& dot_mask)
noexcept -> bool;
52inline auto scan_ipv6_half_avx2(
const char* bytes, uint32_t text_mask,
54 uint32_t& dot_mask)
noexcept ->
bool {
56 _mm256_loadu_si256(
reinterpret_cast<const __m256i*
>(bytes));
57 return classify_ipv6_avx2(chunk, text_mask, colon_mask, dot_mask);
62inline auto classify_ipv6_avx2(__m256i chunk, uint32_t text_mask,
64 uint32_t& dot_mask)
noexcept ->
bool {
65 const auto colon = _mm256_set1_epi8(
':');
66 const auto dot = _mm256_set1_epi8(
'.');
67 const auto slash = _mm256_set1_epi8(
static_cast<char>(
'0' - 1));
68 const auto colon_after_nine = _mm256_set1_epi8(
static_cast<char>(
'9' + 1));
69 const auto before_upper_a = _mm256_set1_epi8(
static_cast<char>(
'A' - 1));
70 const auto after_upper_f = _mm256_set1_epi8(
static_cast<char>(
'F' + 1));
71 const auto before_lower_a = _mm256_set1_epi8(
static_cast<char>(
'a' - 1));
72 const auto after_lower_f = _mm256_set1_epi8(
static_cast<char>(
'f' + 1));
74 const auto is_colon = _mm256_cmpeq_epi8(chunk, colon);
75 const auto is_dot = _mm256_cmpeq_epi8(chunk, dot);
77 _mm256_and_si256(_mm256_cmpgt_epi8(chunk, slash),
78 _mm256_cmpgt_epi8(colon_after_nine, chunk));
80 _mm256_and_si256(_mm256_cmpgt_epi8(chunk, before_upper_a),
81 _mm256_cmpgt_epi8(after_upper_f, chunk));
83 _mm256_and_si256(_mm256_cmpgt_epi8(chunk, before_lower_a),
84 _mm256_cmpgt_epi8(after_lower_f, chunk));
86 _mm256_or_si256(is_digit, _mm256_or_si256(is_upper, is_lower));
87 const auto valid = _mm256_or_si256(is_hex, _mm256_or_si256(is_colon, is_dot));
90 static_cast<uint32_t
>(_mm256_movemask_epi8(is_colon)) & text_mask;
91 dot_mask =
static_cast<uint32_t
>(_mm256_movemask_epi8(is_dot)) & text_mask;
92 const auto valid_mask =
93 static_cast<uint32_t
>(_mm256_movemask_epi8(valid)) & text_mask;
94 return valid_mask == text_mask;
98inline auto scan_ipv6_tail_scalar(
const char* bytes, std::size_t begin,
99 std::size_t end, std::size_t offset,
100 IPv6Scan& scan)
noexcept ->
bool {
101 for (std::size_t i = begin; i < end; ++i) {
102 const char c = bytes[i];
103 const auto bit = uint64_t{1} << (offset + i);
105 scan.colon_mask |= bit;
109 scan.dot_mask |= bit;
124inline auto scan_ipv6_segment_avx2(
const char* bytes, std::size_t len,
125 std::size_t offset,
IPv6Scan& scan)
noexcept
127 const std::size_t full_words = len / 4u;
128 const std::size_t full_bytes = full_words * 4u;
129 if (full_words != 0) {
130 const auto load_mask =
131 _mm256_setr_epi32(full_words > 0 ? -1 : 0, full_words > 1 ? -1 : 0,
132 full_words > 2 ? -1 : 0, full_words > 3 ? -1 : 0,
133 full_words > 4 ? -1 : 0, full_words > 5 ? -1 : 0,
134 full_words > 6 ? -1 : 0, full_words > 7 ? -1 : 0);
136 _mm256_maskload_epi32(
reinterpret_cast<const int*
>(bytes), load_mask);
138 uint32_t colon_mask = 0;
139 uint32_t dot_mask = 0;
141 classify_ipv6_avx2(chunk,
low_mask32(full_bytes), colon_mask, dot_mask);
142 scan.colon_mask |=
static_cast<uint64_t
>(colon_mask) << offset;
143 scan.dot_mask |=
static_cast<uint64_t
>(dot_mask) << offset;
148 return scan_ipv6_tail_scalar(bytes, full_bytes, len, offset, scan);
153inline auto scan_ipv6_avx2(std::string_view text,
IPv6Scan& scan)
noexcept
155 uint32_t colon_lo = 0;
157 if (text.size() >= 32) {
158 if (!scan_ipv6_half_avx2(text.data(), 0xFFFF'FFFFu, colon_lo, dot_lo))
160 scan.colon_mask = colon_lo;
161 scan.dot_mask = dot_lo;
162 return scan_ipv6_segment_avx2(text.data() + 32, text.size() - 32, 32, scan);
165 return scan_ipv6_segment_avx2(text.data(), text.size(), 0, scan);
172 for (std::size_t i = 0; i < text.size(); ++i) {
173 const auto bit = uint64_t{1} << i;
174 const char c = text[i];
176 scan.colon_mask |= bit;
180 scan.dot_mask |= bit;
192 return scan_ipv6_avx2(text, scan);
200 if (piece.empty() || piece.size() > 4)
204 for (
char c : piece) {
207 parsed =
static_cast<uint16_t
>((parsed << 4u) |
hex_value(c));
216 std::size_t end)
noexcept -> std::size_t {
217 const auto mask = scan.colon_mask &
range_mask64(begin, end);
220 return static_cast<std::size_t
>(std::countr_zero(mask));
225 std::string_view tail)
noexcept ->
bool {
227 if (!ipv4 || count > 6)
231 static_cast<uint16_t
>((ipv4->bytes[0] << 8u) | ipv4->bytes[1]);
233 static_cast<uint16_t
>((ipv4->bytes[2] << 8u) | ipv4->bytes[3]);
245 std::array<uint16_t, 8>& groups,
246 uint8_t& count)
noexcept ->
bool {
247 while (begin < end) {
249 const auto piece = text.substr(begin, next - begin);
250 const bool contains_dot = (scan.dot_mask &
range_mask64(begin, next)) != 0;
260 groups[count++] = value;
283[[nodiscard]]
inline auto parse_ipv6(std::string_view text)
noexcept
284 -> std::optional<IPv6Address> {
285 if (text.empty() || text.size() > 45) {
293 std::array<uint16_t, 8> head{};
294 std::array<uint16_t, 8> tail{};
295 uint8_t head_count = 0;
296 uint8_t tail_count = 0;
297 std::optional<std::size_t> compress_at{};
300 if (double_colon_mask != 0) {
301 if (std::popcount(double_colon_mask) != 1)
304 const auto double_colon =
305 static_cast<std::size_t
>(std::countr_zero(double_colon_mask));
306 compress_at = double_colon;
307 if ((double_colon != 0 &&
310 (double_colon + 2 < text.size() &&
312 tail, tail_count))) {
315 if (head_count + tail_count >= 8)
324 std::array<uint16_t, 8> groups{};
325 auto it = groups.begin();
326 it = std::copy(head.begin(), head.begin() + head_count, it);
329 const auto zeros = 8 - head_count - tail_count;
330 it = std::fill_n(it, zeros, uint16_t{0});
331 it = std::copy(tail.begin(), tail.begin() + tail_count, it);
335 for (std::size_t i = 0; i < groups.size(); ++i) {
336 result.
bytes[i * 2] =
static_cast<uint8_t
>(groups[i] >> 8u);
337 result.
bytes[i * 2 + 1] =
static_cast<uint8_t
>(groups[i] & 0xFFu);
Definition classify.hxx:5
auto parse_ipv6_side(std::string_view text, std::size_t begin, std::size_t end, IPv6Scan scan, std::array< uint16_t, 8 > &groups, uint8_t &count) noexcept -> bool
Parse one side of an IPv6 address around optional :: compression.
Definition ipv6.hxx:243
constexpr bool is_hex_digit(char c) noexcept
Return true when c is an ASCII hexadecimal digit.
Definition classify.hxx:8
auto append_ipv4_tail(std::array< uint16_t, 8 > &groups, uint8_t &count, std::string_view tail) noexcept -> bool
Append an embedded IPv4 tail as two IPv6 16-bit groups.
Definition ipv6.hxx:224
auto find_next_colon(IPv6Scan scan, std::size_t begin, std::size_t end) noexcept -> std::size_t
Find the next colon bit in [begin, end).
Definition ipv6.hxx:215
auto scan_ipv6(std::string_view text, IPv6Scan &scan) noexcept -> bool
Scan IPv6 text with the best enabled implementation.
Definition ipv6.hxx:190
auto scan_ipv6_scalar(std::string_view text, IPv6Scan &scan) noexcept -> bool
Scan IPv6 text with portable scalar character classification.
Definition ipv6.hxx:170
constexpr auto range_mask64(std::size_t begin, std::size_t end) noexcept -> uint64_t
Return a mask covering [begin, end) bit positions.
Definition ipv6.hxx:38
bool parse_ipv6_piece(std::string_view piece, uint16_t &value) noexcept
Parse one 1-4 digit IPv6 hexadecimal group.
Definition ipv6.hxx:199
constexpr auto hex_value(char c) noexcept -> uint8_t
Convert an ASCII hexadecimal digit to its numeric value.
Definition classify.hxx:14
constexpr auto low_mask64(std::size_t len) noexcept -> uint64_t
Return a low-bit mask with len bits set.
Definition ipv6.hxx:28
constexpr auto low_mask32(std::size_t len) noexcept -> uint32_t
Return a 32-bit low-bit mask with len bits set.
Definition ipv6.hxx:33
auto parse_ipv4(std::string_view text) noexcept -> std::optional< IPv4Address >
Parse strict dotted-decimal IPv4 text.
Definition ipv4.hxx:129
auto parse_ipv6(std::string_view text) noexcept -> std::optional< IPv6Address >
Parse IPv6 text into sixteen network-order bytes.
Definition ipv6.hxx:283
SIMD feature detection and include for VIPA.
Parsed IPv6 address in network byte order.
Definition address.hxx:26
std::array< uint8_t, 16 > bytes
Definition address.hxx:29
Character-position masks collected while scanning IPv6 text.
Definition ipv6.hxx:20
uint64_t dot_mask
Definition ipv6.hxx:24
uint64_t colon_mask
Definition ipv6.hxx:22