be/src/exec/common/format_ip.h
Line | Count | Source |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // This file is copied from |
18 | | // https://github.com/ClickHouse/ClickHouse/blob/master/src/Common/formatIPv6.h |
19 | | // and modified by Doris |
20 | | |
21 | | #pragma once |
22 | | |
23 | | #include <algorithm> |
24 | | #include <array> |
25 | | #include <bit> |
26 | | #include <cstdint> |
27 | | #include <cstring> |
28 | | #include <utility> |
29 | | |
30 | | #include "core/types.h" |
31 | | #include "exec/common/hex.h" |
32 | | #include "exec/common/string_utils/string_utils.h" |
33 | | |
34 | | constexpr size_t IPV4_BINARY_LENGTH = 4; |
35 | | constexpr size_t IPV4_MAX_TEXT_LENGTH = 15; /// Does not count tail zero byte. |
36 | | constexpr size_t IPV6_MAX_TEXT_LENGTH = 39; |
37 | | constexpr size_t IPV4_MIN_NUM_VALUE = 0; //num value of '0.0.0.0' |
38 | | constexpr size_t IPV4_MAX_NUM_VALUE = 4294967295; //num value of '255.255.255.255' |
39 | | constexpr int IPV4_MAX_OCTET_VALUE = 255; //max value of octet |
40 | | constexpr size_t IPV4_OCTET_BITS = 8; |
41 | | constexpr size_t DECIMAL_BASE = 10; |
42 | | constexpr size_t IPV6_BINARY_LENGTH = 16; |
43 | | |
44 | | namespace doris { |
45 | | |
46 | | extern const std::array<std::pair<const char*, size_t>, 256> one_byte_to_string_lookup_table; |
47 | | |
48 | | /** Format 4-byte binary sequesnce as IPv4 text: 'aaa.bbb.ccc.ddd', |
49 | | * expects in out to be in BE-format, that is 0x7f000001 => "127.0.0.1". |
50 | | * |
51 | | * Any number of the tail bytes can be masked with given mask string. |
52 | | * |
53 | | * Assumptions: |
54 | | * src is IPV4_BINARY_LENGTH long, |
55 | | * dst is IPV4_MAX_TEXT_LENGTH long, |
56 | | * mask_tail_octets <= IPV4_BINARY_LENGTH |
57 | | * mask_string is NON-NULL, if mask_tail_octets > 0. |
58 | | * |
59 | | * Examples: |
60 | | * format_ipv4(&0x7f000001, dst, mask_tail_octets = 0, nullptr); |
61 | | * > dst == "127.0.0.1" |
62 | | * format_ipv4(&0x7f000001, dst, mask_tail_octets = 1, "xxx"); |
63 | | * > dst == "127.0.0.xxx" |
64 | | * format_ipv4(&0x7f000001, dst, mask_tail_octets = 1, "0"); |
65 | | * > dst == "127.0.0.0" |
66 | | */ |
67 | | inline void format_ipv4(const unsigned char* src, size_t src_size, char*& dst, |
68 | 1.50M | uint8_t mask_tail_octets = 0, const char* mask_string = "xxx") { |
69 | 1.50M | const size_t mask_length = mask_string ? strlen(mask_string) : 0; |
70 | 1.50M | const size_t limit = std::min(IPV4_BINARY_LENGTH, IPV4_BINARY_LENGTH - mask_tail_octets); |
71 | 1.50M | const size_t padding = std::min(4 - src_size, limit); |
72 | 1.50M | for (size_t octet = 0; octet < padding; ++octet) { |
73 | 6 | *dst++ = '0'; |
74 | 6 | *dst++ = '.'; |
75 | 6 | } |
76 | | |
77 | 7.53M | for (size_t octet = 4 - src_size; octet < limit; ++octet) { |
78 | 6.02M | uint8_t value = 0; |
79 | | if constexpr (std::endian::native == std::endian::little) |
80 | 6.02M | value = static_cast<uint8_t>(src[IPV4_BINARY_LENGTH - octet - 1]); |
81 | | else |
82 | | value = static_cast<uint8_t>(src[octet]); |
83 | 6.02M | const uint8_t len = static_cast<uint8_t>(one_byte_to_string_lookup_table[value].second); |
84 | 6.02M | const char* str = one_byte_to_string_lookup_table[value].first; |
85 | | |
86 | 6.02M | memcpy(dst, str, len); |
87 | 6.02M | dst += len; |
88 | | |
89 | 6.02M | *dst++ = '.'; |
90 | 6.02M | } |
91 | | |
92 | 1.50M | for (size_t mask = 0; mask < mask_tail_octets; ++mask) { |
93 | 12 | memcpy(dst, mask_string, mask_length); |
94 | 12 | dst += mask_length; |
95 | | |
96 | 12 | *dst++ = '.'; |
97 | 12 | } |
98 | | |
99 | 1.50M | dst--; |
100 | 1.50M | } |
101 | | |
102 | | inline void format_ipv4(const unsigned char* src, char*& dst, uint8_t mask_tail_octets = 0, |
103 | 1.50M | const char* mask_string = "xxx") { |
104 | 1.50M | format_ipv4(src, 4, dst, mask_tail_octets, mask_string); |
105 | 1.50M | } |
106 | | |
107 | | /** Unsafe (no bounds-checking for src nor dst), optimized version of parsing IPv4 string. |
108 | | * |
109 | | * Parses the input string `src` and stores binary host-endian value into buffer pointed by `dst`, |
110 | | * which should be long enough. |
111 | | * That is "127.0.0.1" becomes 0x7f000001. |
112 | | * |
113 | | * In case of failure doesn't modify buffer pointed by `dst`. |
114 | | * |
115 | | * WARNING - this function is adapted to work with ReadBuffer, where src is the position reference (ReadBuffer::position()) |
116 | | * and eof is the ReadBuffer::eof() - therefore algorithm below does not rely on buffer's continuity. |
117 | | * To parse strings use overloads below. |
118 | | * |
119 | | * @param src - iterator (reference to pointer) over input string - warning - continuity is not guaranteed. |
120 | | * @param eof - function returning true if iterator riched the end - warning - can break iterator's continuity. |
121 | | * @param dst - where to put output bytes, expected to be non-null and at IPV4_BINARY_LENGTH-long. |
122 | | * @param first_octet - preparsed first octet |
123 | | * @return - true if parsed successfully, false otherwise. |
124 | | */ |
125 | | template <typename T, typename EOFfunction> |
126 | | requires(std::is_same<typename std::remove_cv<T>::type, char>::value) |
127 | 93.9k | inline bool parse_ipv4(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_octet = -1) { |
128 | 93.9k | if (src == nullptr || first_octet > IPV4_MAX_OCTET_VALUE) { |
129 | 0 | return false; |
130 | 0 | } |
131 | | |
132 | 93.9k | UInt32 result = 0; |
133 | 93.9k | int offset = (IPV4_BINARY_LENGTH - 1) * IPV4_OCTET_BITS; |
134 | 93.9k | if (first_octet >= 0) { |
135 | 403 | result |= first_octet << offset; |
136 | 403 | offset -= IPV4_OCTET_BITS; |
137 | 403 | } |
138 | | |
139 | 356k | for (; true; offset -= IPV4_OCTET_BITS, ++src) { |
140 | 356k | if (eof()) { |
141 | 72 | return false; |
142 | 72 | } |
143 | | |
144 | 356k | UInt32 value = 0; |
145 | 356k | size_t len = 0; |
146 | 893k | while (is_numeric_ascii(*src) && len <= 3) { |
147 | 623k | value = value * DECIMAL_BASE + (*src - '0'); |
148 | 623k | ++len; |
149 | 623k | ++src; |
150 | 623k | if (eof()) { |
151 | 87.5k | break; |
152 | 87.5k | } |
153 | 623k | } |
154 | 356k | if (len == 0 || value > IPV4_MAX_OCTET_VALUE || (offset > 0 && (eof() || *src != '.'))) { |
155 | 6.31k | return false; |
156 | 6.31k | } |
157 | 350k | result |= value << offset; |
158 | | |
159 | 350k | if (offset == 0) { |
160 | 87.5k | break; |
161 | 87.5k | } |
162 | 350k | } |
163 | | |
164 | 87.5k | memcpy(dst, &result, sizeof(result)); |
165 | 87.5k | return true; |
166 | 93.9k | } _ZN5doris10parse_ipv4IKcZNS_10parse_ipv4EPS1_S2_PhEUlvE_Qsr3std7is_sameINSt9remove_cvIT_E4typeEcEE5valueEEbRPS6_T0_S3_i Line | Count | Source | 127 | 93.5k | inline bool parse_ipv4(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_octet = -1) { | 128 | 93.5k | if (src == nullptr || first_octet > IPV4_MAX_OCTET_VALUE) { | 129 | 0 | return false; | 130 | 0 | } | 131 | | | 132 | 93.5k | UInt32 result = 0; | 133 | 93.5k | int offset = (IPV4_BINARY_LENGTH - 1) * IPV4_OCTET_BITS; | 134 | 93.5k | if (first_octet >= 0) { | 135 | 0 | result |= first_octet << offset; | 136 | 0 | offset -= IPV4_OCTET_BITS; | 137 | 0 | } | 138 | | | 139 | 355k | for (; true; offset -= IPV4_OCTET_BITS, ++src) { | 140 | 355k | if (eof()) { | 141 | 72 | return false; | 142 | 72 | } | 143 | | | 144 | 355k | UInt32 value = 0; | 145 | 355k | size_t len = 0; | 146 | 889k | while (is_numeric_ascii(*src) && len <= 3) { | 147 | 621k | value = value * DECIMAL_BASE + (*src - '0'); | 148 | 621k | ++len; | 149 | 621k | ++src; | 150 | 621k | if (eof()) { | 151 | 87.1k | break; | 152 | 87.1k | } | 153 | 621k | } | 154 | 355k | if (len == 0 || value > IPV4_MAX_OCTET_VALUE || (offset > 0 && (eof() || *src != '.'))) { | 155 | 6.31k | return false; | 156 | 6.31k | } | 157 | 349k | result |= value << offset; | 158 | | | 159 | 349k | if (offset == 0) { | 160 | 87.1k | break; | 161 | 87.1k | } | 162 | 349k | } | 163 | | | 164 | 87.1k | memcpy(dst, &result, sizeof(result)); | 165 | 87.1k | return true; | 166 | 93.5k | } |
_ZN5doris10parse_ipv4IKcZNS_10parse_ipv6EPS1_S2_PhEUlvE_Qsr3std7is_sameINSt9remove_cvIT_E4typeEcEE5valueEEbRPS6_T0_S3_i Line | Count | Source | 127 | 403 | inline bool parse_ipv4(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_octet = -1) { | 128 | 403 | if (src == nullptr || first_octet > IPV4_MAX_OCTET_VALUE) { | 129 | 0 | return false; | 130 | 0 | } | 131 | | | 132 | 403 | UInt32 result = 0; | 133 | 403 | int offset = (IPV4_BINARY_LENGTH - 1) * IPV4_OCTET_BITS; | 134 | 403 | if (first_octet >= 0) { | 135 | 403 | result |= first_octet << offset; | 136 | 403 | offset -= IPV4_OCTET_BITS; | 137 | 403 | } | 138 | | | 139 | 1.20k | for (; true; offset -= IPV4_OCTET_BITS, ++src) { | 140 | 1.20k | if (eof()) { | 141 | 0 | return false; | 142 | 0 | } | 143 | | | 144 | 1.20k | UInt32 value = 0; | 145 | 1.20k | size_t len = 0; | 146 | 3.17k | while (is_numeric_ascii(*src) && len <= 3) { | 147 | 2.36k | value = value * DECIMAL_BASE + (*src - '0'); | 148 | 2.36k | ++len; | 149 | 2.36k | ++src; | 150 | 2.36k | if (eof()) { | 151 | 401 | break; | 152 | 401 | } | 153 | 2.36k | } | 154 | 1.20k | if (len == 0 || value > IPV4_MAX_OCTET_VALUE || (offset > 0 && (eof() || *src != '.'))) { | 155 | 4 | return false; | 156 | 4 | } | 157 | 1.20k | result |= value << offset; | 158 | | | 159 | 1.20k | if (offset == 0) { | 160 | 399 | break; | 161 | 399 | } | 162 | 1.20k | } | 163 | | | 164 | 399 | memcpy(dst, &result, sizeof(result)); | 165 | 399 | return true; | 166 | 403 | } |
|
167 | | |
168 | | /// returns pointer to the right after parsed sequence or null on failed parsing |
169 | 93.5k | inline const char* parse_ipv4(const char* src, const char* end, unsigned char* dst) { |
170 | 93.5k | if (parse_ipv4( |
171 | 1.23M | src, [&src, end]() { return src == end; }, dst)) { |
172 | 87.1k | return src; |
173 | 87.1k | } |
174 | 6.39k | return nullptr; |
175 | 93.5k | } |
176 | | |
177 | | /// returns true if whole buffer was parsed successfully |
178 | 93.5k | inline bool parse_ipv4_whole(const char* src, const char* end, unsigned char* dst) { |
179 | 93.5k | return parse_ipv4(src, end, dst) == end; |
180 | 93.5k | } |
181 | | |
182 | | /// integer logarithm, return ceil(log(value, base)) (the smallest integer greater or equal than log(value, base) |
183 | 0 | inline constexpr UInt32 int_log(const UInt32 value, const UInt32 base, const bool carry) { |
184 | 0 | return value >= base ? 1 + int_log(value / base, base, value % base || carry) |
185 | 0 | : value % base > 1 || carry; |
186 | 0 | } |
187 | | |
188 | | /// Print integer in desired base, faster than sprintf. |
189 | | /// NOTE This is not the best way. See https://github.com/miloyip/itoa-benchmark |
190 | | /// But it doesn't matter here. |
191 | | template <UInt32 base, typename T> |
192 | 127k | inline void print_integer(char*& out, T value) { |
193 | 127k | if (value == 0) { |
194 | 178 | *out++ = '0'; |
195 | 127k | } else { |
196 | 127k | constexpr size_t buffer_size = sizeof(T) * int_log(256, base, false); |
197 | | |
198 | 127k | char buf[buffer_size]; |
199 | 127k | auto ptr = buf; |
200 | | |
201 | 526k | while (value > 0) { |
202 | 398k | *ptr = hex_digit_lowercase(value % base); |
203 | 398k | ++ptr; |
204 | 398k | value /= base; |
205 | 398k | } |
206 | | |
207 | | /// Copy to out reversed. |
208 | 526k | while (ptr != buf) { |
209 | 398k | --ptr; |
210 | 398k | *out = *ptr; |
211 | 398k | ++out; |
212 | 398k | } |
213 | 127k | } |
214 | 127k | } |
215 | | |
216 | | /** Rewritten inet_ntop6 from http://svn.apache.org/repos/asf/apr/apr/trunk/network_io/unix/inet_pton.c |
217 | | * performs significantly faster than the reference implementation due to the absence of sprintf calls, |
218 | | * bounds checking, unnecessary string copying and length calculation. |
219 | | * @param src - pointer to IPv6 (16 bytes) stored in little-endian byte order |
220 | | * @param dst - where to put format result bytes |
221 | | * @param zeroed_tail_bytes_count - number of bytes to zero from the address tail |
222 | | */ |
223 | 1.49M | inline void format_ipv6(unsigned char* src, char*& dst, uint8_t zeroed_tail_bytes_count = 0) { |
224 | 1.49M | struct { |
225 | 1.49M | Int64 base, len; |
226 | 1.49M | } best {-1, 0}, cur {-1, 0}; |
227 | 1.49M | std::array<UInt16, IPV6_BINARY_LENGTH / sizeof(UInt16)> words {}; |
228 | | |
229 | | // the current function logic is processed in big endian manner |
230 | | // but ipv6 in doris is stored in little-endian byte order |
231 | | // so transfer to big-endian byte order first |
232 | | // compatible with parse_ipv6 function in format_ip.h |
233 | 1.49M | std::reverse(src, src + IPV6_BINARY_LENGTH); |
234 | | |
235 | | /** Preprocess: |
236 | | * Copy the input (bytewise) array into a wordwise array. |
237 | | * Find the longest run of 0x00's in src[] for :: shorthanding. */ |
238 | 1.49M | const size_t remaining_bytes = IPV6_BINARY_LENGTH - zeroed_tail_bytes_count; |
239 | 13.4M | for (size_t i = 0; i + 1 < remaining_bytes; i += 2) { |
240 | 11.9M | words[i / 2] = static_cast<UInt16>((static_cast<UInt16>(src[i]) << 8) | src[i + 1]); |
241 | 11.9M | } |
242 | 1.49M | if (remaining_bytes % 2 != 0) { |
243 | 3 | words[remaining_bytes / 2] = |
244 | 3 | static_cast<UInt16>(static_cast<UInt16>(src[remaining_bytes - 1]) << 8); |
245 | 3 | } |
246 | | |
247 | 13.4M | for (size_t i = 0; i < words.size(); i++) { |
248 | 11.9M | if (words[i] == 0) { |
249 | 11.8M | if (cur.base == -1) { |
250 | 1.49M | cur.base = i; |
251 | 1.49M | cur.len = 1; |
252 | 10.3M | } else { |
253 | 10.3M | cur.len++; |
254 | 10.3M | } |
255 | 11.8M | } else { |
256 | 127k | if (cur.base != -1) { |
257 | 34.5k | if (best.base == -1 || cur.len > best.len) { |
258 | 34.5k | best = cur; |
259 | 34.5k | } |
260 | 34.5k | cur.base = -1; |
261 | 34.5k | } |
262 | 127k | } |
263 | 11.9M | } |
264 | | |
265 | 1.49M | if (cur.base != -1) { |
266 | 1.45M | if (best.base == -1 || cur.len > best.len) { |
267 | 1.45M | best = cur; |
268 | 1.45M | } |
269 | 1.45M | } |
270 | 1.49M | if (best.base != -1 && best.len < 2) { |
271 | 61 | best.base = -1; |
272 | 61 | } |
273 | | |
274 | | /// Format the result. |
275 | 13.4M | for (size_t i = 0; i < words.size(); i++) { |
276 | | /// Are we inside the best run of 0x00's? |
277 | 11.9M | if (best.base != -1) { |
278 | 11.9M | auto best_base = static_cast<size_t>(best.base); |
279 | 11.9M | if (i >= best_base && i < (best_base + best.len)) { |
280 | 11.8M | if (i == best_base) { |
281 | 1.49M | *dst++ = ':'; |
282 | 1.49M | } |
283 | 11.8M | continue; |
284 | 11.8M | } |
285 | 11.9M | } |
286 | | /// Are we following an initial run of 0x00s or any real hex? |
287 | 127k | if (i != 0) { |
288 | 91.4k | *dst++ = ':'; |
289 | 91.4k | } |
290 | | /// Is this address an encapsulated IPv4? |
291 | 127k | if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffffu))) { |
292 | 84 | uint8_t ipv4_buffer[IPV4_BINARY_LENGTH] = {0}; |
293 | 84 | memcpy(ipv4_buffer, src + 12, IPV4_BINARY_LENGTH); |
294 | | // Due to historical reasons format_ipv4() takes ipv4 in BE format, but inside ipv6 we store it in LE-format. |
295 | 84 | if constexpr (std::endian::native == std::endian::little) { |
296 | 84 | std::reverse(std::begin(ipv4_buffer), std::end(ipv4_buffer)); |
297 | 84 | } |
298 | 84 | format_ipv4(ipv4_buffer, dst, |
299 | 84 | std::min(zeroed_tail_bytes_count, static_cast<uint8_t>(IPV4_BINARY_LENGTH)), |
300 | 84 | "0"); |
301 | | // format_ipv4 has already added a null-terminator for us. |
302 | 84 | return; |
303 | 84 | } |
304 | 127k | print_integer<16>(dst, words[i]); |
305 | 127k | } |
306 | | |
307 | | /// Was it a trailing run of 0x00's? |
308 | 1.49M | if (best.base != -1 && |
309 | 1.49M | static_cast<size_t>(best.base) + static_cast<size_t>(best.len) == words.size()) { |
310 | 1.45M | *dst++ = ':'; |
311 | 1.45M | } |
312 | 1.49M | } |
313 | | |
314 | | /** Unsafe (no bounds-checking for src nor dst), optimized version of parsing IPv6 string. |
315 | | * |
316 | | * Parses the input string `src` and stores binary little-endian value into buffer pointed by `dst`, |
317 | | * which should be long enough. In case of failure zeroes IPV6_BINARY_LENGTH bytes of buffer pointed by `dst`. |
318 | | * |
319 | | * WARNING - this function is adapted to work with ReadBuffer, where src is the position reference (ReadBuffer::position()) |
320 | | * and eof is the ReadBuffer::eof() - therefore algorithm below does not rely on buffer's continuity. |
321 | | * To parse strings use overloads below. |
322 | | * |
323 | | * @param src - iterator (reference to pointer) over input string - warning - continuity is not guaranteed. |
324 | | * @param eof - function returning true if iterator riched the end - warning - can break iterator's continuity. |
325 | | * @param dst - where to put output bytes in little-endian byte order, expected to be non-null and at IPV6_BINARY_LENGTH-long. |
326 | | * @param first_block - preparsed first block |
327 | | * @return - true if parsed successfully, false otherwise. |
328 | | */ |
329 | | template <typename T, typename EOFfunction> |
330 | | requires(std::is_same<typename std::remove_cv<T>::type, char>::value) |
331 | 70.8k | inline bool parse_ipv6(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_block = -1) { |
332 | 70.8k | const auto clear_dst = [dst]() { |
333 | 8.09k | std::memset(dst, '\0', IPV6_BINARY_LENGTH); |
334 | 8.09k | return false; |
335 | 8.09k | }; |
336 | | |
337 | 70.8k | if (src == nullptr || eof()) return clear_dst(); |
338 | | |
339 | 70.8k | int groups = 0; /// number of parsed groups |
340 | 70.8k | unsigned char* iter = dst; /// iterator over dst buffer |
341 | 70.8k | unsigned char* zptr = |
342 | 70.8k | nullptr; /// pointer into dst buffer array where all-zeroes block ("::") is started |
343 | | |
344 | 70.8k | std::memset(dst, '\0', IPV6_BINARY_LENGTH); |
345 | | |
346 | 70.8k | if (first_block >= 0) { |
347 | 0 | *iter++ = static_cast<unsigned char>((first_block >> 8) & 0xffu); |
348 | 0 | *iter++ = static_cast<unsigned char>(first_block & 0xffu); |
349 | 0 | if (*src == ':') { |
350 | 0 | zptr = iter; |
351 | 0 | ++src; |
352 | 0 | } |
353 | 0 | ++groups; |
354 | 0 | } |
355 | | |
356 | 70.8k | bool group_start = true; |
357 | | |
358 | 542k | while (!eof() && groups < 8) { |
359 | 476k | if (*src == ':') { |
360 | 397k | ++src; |
361 | 397k | if (eof()) /// trailing colon is not allowed |
362 | 32 | return clear_dst(); |
363 | | |
364 | 397k | group_start = true; |
365 | | |
366 | 397k | if (*src == ':') { |
367 | 8.73k | if (zptr != nullptr) /// multiple all-zeroes blocks are not allowed |
368 | 159 | return clear_dst(); |
369 | 8.57k | zptr = iter; |
370 | 8.57k | ++src; |
371 | 8.57k | if (!eof() && *src == ':') { |
372 | | /// more than one all-zeroes block is not allowed |
373 | 10 | return clear_dst(); |
374 | 10 | } |
375 | 8.56k | continue; |
376 | 8.57k | } |
377 | 389k | if (groups == 0) /// leading colon is not allowed |
378 | 0 | return clear_dst(); |
379 | 389k | } |
380 | | |
381 | | /// mixed IPv4 parsing |
382 | 467k | if (*src == '.') { |
383 | 1.00k | if (groups <= 1 && zptr == nullptr) /// IPv4 block can't be the first |
384 | 602 | return clear_dst(); |
385 | | |
386 | 402 | if (group_start) /// first octet of IPv4 should be already parsed as an IPv6 group |
387 | 0 | return clear_dst(); |
388 | | |
389 | 402 | ++src; |
390 | 402 | if (eof()) return clear_dst(); |
391 | | |
392 | | /// last parsed group should be reinterpreted as a decimal value - it's the first octet of IPv4 |
393 | 402 | --groups; |
394 | 402 | iter -= 2; |
395 | | |
396 | 402 | UInt16 num = 0; |
397 | 1.20k | for (int i = 0; i < 2; ++i) { |
398 | 803 | unsigned char first = (iter[i] >> 4) & 0x0fu; |
399 | 803 | unsigned char second = iter[i] & 0x0fu; |
400 | 803 | if (first > 9 || second > 9) return clear_dst(); |
401 | 803 | (num *= 100) += first * 10 + second; |
402 | 803 | } |
403 | 402 | if (num > 255) return clear_dst(); |
404 | | |
405 | | /// parse IPv4 with known first octet |
406 | 402 | if (!parse_ipv4(src, eof, iter, num)) return clear_dst(); |
407 | | |
408 | | if constexpr (std::endian::native == std::endian::little) |
409 | 398 | std::reverse(iter, iter + IPV4_BINARY_LENGTH); |
410 | | |
411 | 398 | iter += 4; |
412 | 398 | groups += 2; |
413 | 398 | break; /// IPv4 block is the last - end of parsing |
414 | 402 | } |
415 | | |
416 | 466k | if (!group_start) /// end of parsing |
417 | 835 | break; |
418 | 465k | group_start = false; |
419 | | |
420 | 465k | UInt16 val = 0; /// current decoded group |
421 | 465k | int xdigits = 0; /// number of decoded hex digits in current group |
422 | | |
423 | 1.41M | for (; !eof() && xdigits < 4; ++src, ++xdigits) { |
424 | 1.21M | UInt8 num = unhex(*src); |
425 | 1.21M | if (num == 0xFF) break; |
426 | 949k | (val <<= 4) |= num; |
427 | 949k | } |
428 | | |
429 | 465k | if (xdigits == 0) /// end of parsing |
430 | 2.95k | break; |
431 | | |
432 | 462k | *iter++ = static_cast<unsigned char>((val >> 8) & 0xffu); |
433 | 462k | *iter++ = static_cast<unsigned char>(val & 0xffu); |
434 | 462k | ++groups; |
435 | 462k | } |
436 | | |
437 | | /// either all 8 groups or all-zeroes block should be present |
438 | 70.0k | if (groups < 8 && zptr == nullptr) return clear_dst(); |
439 | | |
440 | | /// process all-zeroes block |
441 | 62.7k | if (zptr != nullptr) { |
442 | 8.36k | if (groups == 8) { |
443 | | /// all-zeroes block at least should be one |
444 | | /// 2001:0db8:86a3::08d3:1319:8a2e:0370:7344 not valid |
445 | 4 | return clear_dst(); |
446 | 4 | } |
447 | 8.35k | size_t msize = iter - zptr; |
448 | 8.35k | std::memmove(dst + IPV6_BINARY_LENGTH - msize, zptr, msize); |
449 | 8.35k | std::memset(zptr, '\0', IPV6_BINARY_LENGTH - (iter - dst)); |
450 | 8.35k | } |
451 | | |
452 | | /// the current function logic is processed in big endian manner |
453 | | /// but ipv6 in doris is stored in little-endian byte order |
454 | | /// so transfer to little-endian |
455 | 62.7k | std::reverse(dst, dst + IPV6_BINARY_LENGTH); |
456 | | |
457 | 62.7k | return true; |
458 | 62.7k | } |
459 | | |
460 | | /// returns pointer to the right after parsed sequence or null on failed parsing |
461 | 70.8k | inline const char* parse_ipv6(const char* src, const char* end, unsigned char* dst) { |
462 | 70.8k | if (parse_ipv6( |
463 | 2.44M | src, [&src, end]() { return src == end; }, dst)) |
464 | 62.7k | return src; |
465 | 8.08k | return nullptr; |
466 | 70.8k | } |
467 | | |
468 | | /// returns true if whole buffer was parsed successfully |
469 | 70.8k | inline bool parse_ipv6_whole(const char* src, const char* end, unsigned char* dst) { |
470 | 70.8k | return parse_ipv6(src, end, dst) == end; |
471 | 70.8k | } |
472 | | |
473 | | } // namespace doris |