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 | 94.0k | inline bool parse_ipv4(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_octet = -1) { |
128 | 94.0k | if (src == nullptr || first_octet > IPV4_MAX_OCTET_VALUE) { |
129 | 0 | return false; |
130 | 0 | } |
131 | | |
132 | 94.0k | UInt32 result = 0; |
133 | 94.0k | int offset = (IPV4_BINARY_LENGTH - 1) * IPV4_OCTET_BITS; |
134 | 94.0k | if (first_octet >= 0) { |
135 | 404 | result |= first_octet << offset; |
136 | 404 | offset -= IPV4_OCTET_BITS; |
137 | 404 | } |
138 | | |
139 | 357k | for (; true; offset -= IPV4_OCTET_BITS, ++src) { |
140 | 357k | if (eof()) { |
141 | 72 | return false; |
142 | 72 | } |
143 | | |
144 | 357k | UInt32 value = 0; |
145 | 357k | size_t len = 0; |
146 | 893k | while (is_numeric_ascii(*src) && len <= 3) { |
147 | 624k | value = value * DECIMAL_BASE + (*src - '0'); |
148 | 624k | ++len; |
149 | 624k | ++src; |
150 | 624k | if (eof()) { |
151 | 87.6k | break; |
152 | 87.6k | } |
153 | 624k | } |
154 | 357k | if (len == 0 || value > IPV4_MAX_OCTET_VALUE || (offset > 0 && (eof() || *src != '.'))) { |
155 | 6.34k | return false; |
156 | 6.34k | } |
157 | 350k | result |= value << offset; |
158 | | |
159 | 350k | if (offset == 0) { |
160 | 87.6k | break; |
161 | 87.6k | } |
162 | 350k | } |
163 | | |
164 | 87.6k | memcpy(dst, &result, sizeof(result)); |
165 | 87.6k | return true; |
166 | 94.0k | } _ZN5doris10parse_ipv4IKcZNS_10parse_ipv4EPS1_S2_PhEUlvE_Qsr3std7is_sameINSt9remove_cvIT_E4typeEcEE5valueEEbRPS6_T0_S3_i Line | Count | Source | 127 | 93.6k | inline bool parse_ipv4(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_octet = -1) { | 128 | 93.6k | if (src == nullptr || first_octet > IPV4_MAX_OCTET_VALUE) { | 129 | 0 | return false; | 130 | 0 | } | 131 | | | 132 | 93.6k | UInt32 result = 0; | 133 | 93.6k | int offset = (IPV4_BINARY_LENGTH - 1) * IPV4_OCTET_BITS; | 134 | 93.6k | if (first_octet >= 0) { | 135 | 0 | result |= first_octet << offset; | 136 | 0 | offset -= IPV4_OCTET_BITS; | 137 | 0 | } | 138 | | | 139 | 356k | for (; true; offset -= IPV4_OCTET_BITS, ++src) { | 140 | 356k | if (eof()) { | 141 | 72 | return false; | 142 | 72 | } | 143 | | | 144 | 355k | UInt32 value = 0; | 145 | 355k | size_t len = 0; | 146 | 890k | 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.2k | break; | 152 | 87.2k | } | 153 | 621k | } | 154 | 355k | if (len == 0 || value > IPV4_MAX_OCTET_VALUE || (offset > 0 && (eof() || *src != '.'))) { | 155 | 6.34k | return false; | 156 | 6.34k | } | 157 | 349k | result |= value << offset; | 158 | | | 159 | 349k | if (offset == 0) { | 160 | 87.2k | break; | 161 | 87.2k | } | 162 | 349k | } | 163 | | | 164 | 87.2k | memcpy(dst, &result, sizeof(result)); | 165 | 87.2k | return true; | 166 | 93.6k | } |
_ZN5doris10parse_ipv4IKcZNS_10parse_ipv6EPS1_S2_PhEUlvE_Qsr3std7is_sameINSt9remove_cvIT_E4typeEcEE5valueEEbRPS6_T0_S3_i Line | Count | Source | 127 | 405 | inline bool parse_ipv4(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_octet = -1) { | 128 | 405 | if (src == nullptr || first_octet > IPV4_MAX_OCTET_VALUE) { | 129 | 0 | return false; | 130 | 0 | } | 131 | | | 132 | 405 | UInt32 result = 0; | 133 | 405 | int offset = (IPV4_BINARY_LENGTH - 1) * IPV4_OCTET_BITS; | 134 | 405 | if (first_octet >= 0) { | 135 | 404 | result |= first_octet << offset; | 136 | 404 | offset -= IPV4_OCTET_BITS; | 137 | 404 | } | 138 | | | 139 | 1.21k | for (; true; offset -= IPV4_OCTET_BITS, ++src) { | 140 | 1.21k | if (eof()) { | 141 | 0 | return false; | 142 | 0 | } | 143 | | | 144 | 1.21k | UInt32 value = 0; | 145 | 1.21k | size_t len = 0; | 146 | 3.19k | while (is_numeric_ascii(*src) && len <= 3) { | 147 | 2.38k | value = value * DECIMAL_BASE + (*src - '0'); | 148 | 2.38k | ++len; | 149 | 2.38k | ++src; | 150 | 2.38k | if (eof()) { | 151 | 403 | break; | 152 | 403 | } | 153 | 2.38k | } | 154 | 1.21k | 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 | 401 | break; | 161 | 401 | } | 162 | 1.20k | } | 163 | | | 164 | 401 | memcpy(dst, &result, sizeof(result)); | 165 | 401 | return true; | 166 | 405 | } |
|
167 | | |
168 | | /// returns pointer to the right after parsed sequence or null on failed parsing |
169 | 93.6k | inline const char* parse_ipv4(const char* src, const char* end, unsigned char* dst) { |
170 | 93.6k | if (parse_ipv4( |
171 | 1.24M | src, [&src, end]() { return src == end; }, dst)) { |
172 | 87.2k | return src; |
173 | 87.2k | } |
174 | 6.41k | return nullptr; |
175 | 93.6k | } |
176 | | |
177 | | /// returns true if whole buffer was parsed successfully |
178 | 93.6k | inline bool parse_ipv4_whole(const char* src, const char* end, unsigned char* dst) { |
179 | 93.6k | return parse_ipv4(src, end, dst) == end; |
180 | 93.6k | } |
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 | 177 | *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 | 399k | *ptr = hex_digit_lowercase(value % base); |
203 | 399k | ++ptr; |
204 | 399k | value /= base; |
205 | 399k | } |
206 | | |
207 | | /// Copy to out reversed. |
208 | 526k | while (ptr != buf) { |
209 | 399k | --ptr; |
210 | 399k | *out = *ptr; |
211 | 399k | ++out; |
212 | 399k | } |
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 - the parameter is currently not being used |
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 | 13.4M | for (size_t i = 0; i < (IPV6_BINARY_LENGTH - zeroed_tail_bytes_count); i += 2) { |
239 | 11.9M | words[i / 2] = (uint16_t)(src[i] << 8) | src[i + 1]; |
240 | 11.9M | } |
241 | | |
242 | 13.4M | for (size_t i = 0; i < words.size(); i++) { |
243 | 11.9M | if (words[i] == 0) { |
244 | 11.8M | if (cur.base == -1) { |
245 | 1.49M | cur.base = i; |
246 | 1.49M | cur.len = 1; |
247 | 10.3M | } else { |
248 | 10.3M | cur.len++; |
249 | 10.3M | } |
250 | 11.8M | } else { |
251 | 127k | if (cur.base != -1) { |
252 | 34.6k | if (best.base == -1 || cur.len > best.len) { |
253 | 34.6k | best = cur; |
254 | 34.6k | } |
255 | 34.6k | cur.base = -1; |
256 | 34.6k | } |
257 | 127k | } |
258 | 11.9M | } |
259 | | |
260 | 1.49M | if (cur.base != -1) { |
261 | 1.45M | if (best.base == -1 || cur.len > best.len) { |
262 | 1.45M | best = cur; |
263 | 1.45M | } |
264 | 1.45M | } |
265 | 1.49M | if (best.base != -1 && best.len < 2) { |
266 | 60 | best.base = -1; |
267 | 60 | } |
268 | | |
269 | | /// Format the result. |
270 | 13.4M | for (size_t i = 0; i < words.size(); i++) { |
271 | | /// Are we inside the best run of 0x00's? |
272 | 11.9M | if (best.base != -1) { |
273 | 11.9M | auto best_base = static_cast<size_t>(best.base); |
274 | 11.9M | if (i >= best_base && i < (best_base + best.len)) { |
275 | 11.8M | if (i == best_base) { |
276 | 1.49M | *dst++ = ':'; |
277 | 1.49M | } |
278 | 11.8M | continue; |
279 | 11.8M | } |
280 | 11.9M | } |
281 | | /// Are we following an initial run of 0x00s or any real hex? |
282 | 127k | if (i != 0) { |
283 | 91.5k | *dst++ = ':'; |
284 | 91.5k | } |
285 | | /// Is this address an encapsulated IPv4? |
286 | 127k | if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffffu))) { |
287 | 84 | uint8_t ipv4_buffer[IPV4_BINARY_LENGTH] = {0}; |
288 | 84 | memcpy(ipv4_buffer, src + 12, IPV4_BINARY_LENGTH); |
289 | | // Due to historical reasons format_ipv4() takes ipv4 in BE format, but inside ipv6 we store it in LE-format. |
290 | 84 | if constexpr (std::endian::native == std::endian::little) { |
291 | 84 | std::reverse(std::begin(ipv4_buffer), std::end(ipv4_buffer)); |
292 | 84 | } |
293 | 84 | format_ipv4(ipv4_buffer, dst, |
294 | 84 | std::min(zeroed_tail_bytes_count, static_cast<uint8_t>(IPV4_BINARY_LENGTH)), |
295 | 84 | "0"); |
296 | | // format_ipv4 has already added a null-terminator for us. |
297 | 84 | return; |
298 | 84 | } |
299 | 127k | print_integer<16>(dst, words[i]); |
300 | 127k | } |
301 | | |
302 | | /// Was it a trailing run of 0x00's? |
303 | 1.49M | if (best.base != -1 && |
304 | 1.49M | static_cast<size_t>(best.base) + static_cast<size_t>(best.len) == words.size()) { |
305 | 1.45M | *dst++ = ':'; |
306 | 1.45M | } |
307 | 1.49M | } |
308 | | |
309 | | /** Unsafe (no bounds-checking for src nor dst), optimized version of parsing IPv6 string. |
310 | | * |
311 | | * Parses the input string `src` and stores binary little-endian value into buffer pointed by `dst`, |
312 | | * which should be long enough. In case of failure zeroes IPV6_BINARY_LENGTH bytes of buffer pointed by `dst`. |
313 | | * |
314 | | * WARNING - this function is adapted to work with ReadBuffer, where src is the position reference (ReadBuffer::position()) |
315 | | * and eof is the ReadBuffer::eof() - therefore algorithm below does not rely on buffer's continuity. |
316 | | * To parse strings use overloads below. |
317 | | * |
318 | | * @param src - iterator (reference to pointer) over input string - warning - continuity is not guaranteed. |
319 | | * @param eof - function returning true if iterator riched the end - warning - can break iterator's continuity. |
320 | | * @param dst - where to put output bytes in little-endian byte order, expected to be non-null and at IPV6_BINARY_LENGTH-long. |
321 | | * @param first_block - preparsed first block |
322 | | * @return - true if parsed successfully, false otherwise. |
323 | | */ |
324 | | template <typename T, typename EOFfunction> |
325 | | requires(std::is_same<typename std::remove_cv<T>::type, char>::value) |
326 | 70.8k | inline bool parse_ipv6(T*& src, EOFfunction eof, unsigned char* dst, int32_t first_block = -1) { |
327 | 70.8k | const auto clear_dst = [dst]() { |
328 | 8.10k | std::memset(dst, '\0', IPV6_BINARY_LENGTH); |
329 | 8.10k | return false; |
330 | 8.10k | }; |
331 | | |
332 | 70.8k | if (src == nullptr || eof()) return clear_dst(); |
333 | | |
334 | 70.8k | int groups = 0; /// number of parsed groups |
335 | 70.8k | unsigned char* iter = dst; /// iterator over dst buffer |
336 | 70.8k | unsigned char* zptr = |
337 | 70.8k | nullptr; /// pointer into dst buffer array where all-zeroes block ("::") is started |
338 | | |
339 | 70.8k | std::memset(dst, '\0', IPV6_BINARY_LENGTH); |
340 | | |
341 | 70.8k | if (first_block >= 0) { |
342 | 0 | *iter++ = static_cast<unsigned char>((first_block >> 8) & 0xffu); |
343 | 0 | *iter++ = static_cast<unsigned char>(first_block & 0xffu); |
344 | 0 | if (*src == ':') { |
345 | 0 | zptr = iter; |
346 | 0 | ++src; |
347 | 0 | } |
348 | 0 | ++groups; |
349 | 0 | } |
350 | | |
351 | 70.8k | bool group_start = true; |
352 | | |
353 | 542k | while (!eof() && groups < 8) { |
354 | 476k | if (*src == ':') { |
355 | 397k | ++src; |
356 | 397k | if (eof()) /// trailing colon is not allowed |
357 | 32 | return clear_dst(); |
358 | | |
359 | 397k | group_start = true; |
360 | | |
361 | 397k | if (*src == ':') { |
362 | 8.73k | if (zptr != nullptr) /// multiple all-zeroes blocks are not allowed |
363 | 159 | return clear_dst(); |
364 | 8.57k | zptr = iter; |
365 | 8.57k | ++src; |
366 | 8.57k | if (!eof() && *src == ':') { |
367 | | /// more than one all-zeroes block is not allowed |
368 | 10 | return clear_dst(); |
369 | 10 | } |
370 | 8.56k | continue; |
371 | 8.57k | } |
372 | 389k | if (groups == 0) /// leading colon is not allowed |
373 | 0 | return clear_dst(); |
374 | 389k | } |
375 | | |
376 | | /// mixed IPv4 parsing |
377 | 467k | if (*src == '.') { |
378 | 1.01k | if (groups <= 1 && zptr == nullptr) /// IPv4 block can't be the first |
379 | 605 | return clear_dst(); |
380 | | |
381 | 405 | if (group_start) /// first octet of IPv4 should be already parsed as an IPv6 group |
382 | 0 | return clear_dst(); |
383 | | |
384 | 405 | ++src; |
385 | 405 | if (eof()) return clear_dst(); |
386 | | |
387 | | /// last parsed group should be reinterpreted as a decimal value - it's the first octet of IPv4 |
388 | 405 | --groups; |
389 | 405 | iter -= 2; |
390 | | |
391 | 405 | UInt16 num = 0; |
392 | 1.21k | for (int i = 0; i < 2; ++i) { |
393 | 811 | unsigned char first = (iter[i] >> 4) & 0x0fu; |
394 | 811 | unsigned char second = iter[i] & 0x0fu; |
395 | 811 | if (first > 9 || second > 9) return clear_dst(); |
396 | 811 | (num *= 100) += first * 10 + second; |
397 | 811 | } |
398 | 405 | if (num > 255) return clear_dst(); |
399 | | |
400 | | /// parse IPv4 with known first octet |
401 | 405 | if (!parse_ipv4(src, eof, iter, num)) return clear_dst(); |
402 | | |
403 | | if constexpr (std::endian::native == std::endian::little) |
404 | 401 | std::reverse(iter, iter + IPV4_BINARY_LENGTH); |
405 | | |
406 | 401 | iter += 4; |
407 | 401 | groups += 2; |
408 | 401 | break; /// IPv4 block is the last - end of parsing |
409 | 405 | } |
410 | | |
411 | 466k | if (!group_start) /// end of parsing |
412 | 835 | break; |
413 | 465k | group_start = false; |
414 | | |
415 | 465k | UInt16 val = 0; /// current decoded group |
416 | 465k | int xdigits = 0; /// number of decoded hex digits in current group |
417 | | |
418 | 1.41M | for (; !eof() && xdigits < 4; ++src, ++xdigits) { |
419 | 1.21M | UInt8 num = unhex(*src); |
420 | 1.21M | if (num == 0xFF) break; |
421 | 949k | (val <<= 4) |= num; |
422 | 949k | } |
423 | | |
424 | 465k | if (xdigits == 0) /// end of parsing |
425 | 2.96k | break; |
426 | | |
427 | 462k | *iter++ = static_cast<unsigned char>((val >> 8) & 0xffu); |
428 | 462k | *iter++ = static_cast<unsigned char>(val & 0xffu); |
429 | 462k | ++groups; |
430 | 462k | } |
431 | | |
432 | | /// either all 8 groups or all-zeroes block should be present |
433 | 70.0k | if (groups < 8 && zptr == nullptr) return clear_dst(); |
434 | | |
435 | | /// process all-zeroes block |
436 | 62.7k | if (zptr != nullptr) { |
437 | 8.36k | if (groups == 8) { |
438 | | /// all-zeroes block at least should be one |
439 | | /// 2001:0db8:86a3::08d3:1319:8a2e:0370:7344 not valid |
440 | 4 | return clear_dst(); |
441 | 4 | } |
442 | 8.35k | size_t msize = iter - zptr; |
443 | 8.35k | std::memmove(dst + IPV6_BINARY_LENGTH - msize, zptr, msize); |
444 | 8.35k | std::memset(zptr, '\0', IPV6_BINARY_LENGTH - (iter - dst)); |
445 | 8.35k | } |
446 | | |
447 | | /// the current function logic is processed in big endian manner |
448 | | /// but ipv6 in doris is stored in little-endian byte order |
449 | | /// so transfer to little-endian |
450 | 62.7k | std::reverse(dst, dst + IPV6_BINARY_LENGTH); |
451 | | |
452 | 62.7k | return true; |
453 | 62.7k | } |
454 | | |
455 | | /// returns pointer to the right after parsed sequence or null on failed parsing |
456 | 70.8k | inline const char* parse_ipv6(const char* src, const char* end, unsigned char* dst) { |
457 | 70.8k | if (parse_ipv6( |
458 | 2.44M | src, [&src, end]() { return src == end; }, dst)) |
459 | 62.7k | return src; |
460 | 8.10k | return nullptr; |
461 | 70.8k | } |
462 | | |
463 | | /// returns true if whole buffer was parsed successfully |
464 | 70.8k | inline bool parse_ipv6_whole(const char* src, const char* end, unsigned char* dst) { |
465 | 70.8k | return parse_ipv6(src, end, dst) == end; |
466 | 70.8k | } |
467 | | |
468 | | } // namespace doris |