Mask the substring in range [start, end) of the given string value. Using rules in 'MaskTransform'. Indices are counted in UTF-8 code points.
| 124 | /// Mask the substring in range [start, end) of the given string value. Using rules in |
| 125 | /// 'MaskTransform'. Indices are counted in UTF-8 code points. |
| 126 | static StringVal MaskSubStrUtf8(FunctionContext* ctx, const StringVal& val, |
| 127 | int start, int end, int masked_upper_char, int masked_lower_char, |
| 128 | int masked_digit_char, int masked_other_char) { |
| 129 | DCHECK_GE(start, 0); |
| 130 | DCHECK_LT(start, end); |
| 131 | DCHECK_LE(end, val.len); |
| 132 | const char* p_start = reinterpret_cast<char*>(val.ptr); |
| 133 | const char* p_end = p_start + val.len; |
| 134 | const char* p = p_start; |
| 135 | utf8_codecvt<char>::state_type cvt_state; |
| 136 | int char_cnt = 0; |
| 137 | // Skip leading 'start' code points. Leading bytes will be copied directly. |
| 138 | while (char_cnt < start && p != p_end) { |
| 139 | uint32_t codepoint = utf8_codecvt<char>::to_unicode(cvt_state, p, p_end); |
| 140 | if (CheckAndWarnCodePoint(ctx, codepoint)) return StringVal::null(); |
| 141 | ++char_cnt; |
| 142 | } |
| 143 | // Calculating the result length in bytes. |
| 144 | int result_bytes = p - p_start; |
| 145 | int leading_bytes = result_bytes; |
| 146 | // Collect code points at range [start, end - 1) and mask them. |
| 147 | vector<uint32_t> masked_code_points; |
| 148 | // Create unicode locale for checking upper/lower cases or digits. |
| 149 | static const std::locale& loc = boost::locale::generator()("en_US.UTF-8"); |
| 150 | // Check facet existence to avoid predicates throws exception. |
| 151 | if (!std::has_facet<std::ctype<wchar_t>>(loc)) { |
| 152 | ctx->SetError("Cannot mask unicode strings since locale en_US.UTF-8 not found!"); |
| 153 | return StringVal(); |
| 154 | } |
| 155 | while (char_cnt < end && p != p_end) { |
| 156 | // Parse and get the first code point in string range [p, p_end). |
| 157 | // 'to_unicode' will update the pointer 'p'. |
| 158 | uint32_t codepoint = utf8_codecvt<char>::to_unicode(cvt_state, p, p_end); |
| 159 | if (CheckAndWarnCodePoint(ctx, codepoint)) return StringVal::null(); |
| 160 | codepoint = MaskTransform(codepoint, masked_upper_char, masked_lower_char, |
| 161 | masked_digit_char, masked_other_char, &loc); |
| 162 | masked_code_points.push_back(codepoint); |
| 163 | result_bytes += utf::utf_traits<char>::width(codepoint); |
| 164 | ++char_cnt; |
| 165 | } |
| 166 | // Trailing bytes will be copied directly without masking. |
| 167 | int tail_len = p_end - p; |
| 168 | result_bytes += tail_len; |
| 169 | |
| 170 | StringVal result(ctx, result_bytes); |
| 171 | if (UNLIKELY(result.is_null)) return result; |
| 172 | // Copy leading bytes. |
| 173 | Ubsan::MemCpy(result.ptr, val.ptr, leading_bytes); |
| 174 | // Converting masked code points to UTF-8 encoded bytes. |
| 175 | char* ptr = reinterpret_cast<char*>(result.ptr) + leading_bytes; |
| 176 | p_end = reinterpret_cast<char*>(result.ptr) + result_bytes; |
| 177 | for (uint32_t c : masked_code_points) { |
| 178 | uint32_t width = utf8_codecvt<char>::from_unicode(cvt_state, c, ptr, p_end); |
| 179 | DCHECK(width != utf::illegal && width != utf::incomplete); |
| 180 | ptr += width; |
| 181 | DCHECK(ptr <= p_end); |
| 182 | } |
| 183 | // Copy trailing bytes. |
no test coverage detected