be/src/exprs/function/function_levenshtein.cpp
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 | | |
18 | | #include <algorithm> |
19 | | #include <array> |
20 | | #include <limits> |
21 | | #include <string_view> |
22 | | #include <unordered_map> |
23 | | #include <vector> |
24 | | |
25 | | #include "common/status.h" |
26 | | #include "core/custom_allocator.h" |
27 | | #include "core/data_type/data_type_number.h" |
28 | | #include "core/pod_array.h" |
29 | | #include "core/string_ref.h" |
30 | | #include "exprs/function/function_totype.h" |
31 | | #include "exprs/function/simple_function_factory.h" |
32 | | #include "util/simd/vstring_function.h" |
33 | | |
34 | | namespace doris { |
35 | | |
36 | | struct NameLevenshtein { |
37 | | static constexpr auto name = "levenshtein"; |
38 | | }; |
39 | | |
40 | | struct NameDamerauLevenshteinDistance { |
41 | | static constexpr auto name = "damerau_levenshtein_distance"; |
42 | | }; |
43 | | |
44 | | // 64MB limit for the distance matrix. |
45 | | static constexpr size_t MAX_DAMERAU_LEVENSHTEIN_MATRIX_CELLS = 16 * 1024 * 1024; |
46 | | |
47 | | using Utf8Offsets = DorisVector<size_t>; |
48 | | |
49 | | static StringRef string_ref_at(const ColumnString::Chars& data, |
50 | 182 | const ColumnString::Offsets& offsets, size_t i) { |
51 | 182 | DCHECK_LT(i, offsets.size()); |
52 | 182 | const auto previous_offset = i == 0 ? 0 : offsets[i - 1]; |
53 | 182 | return StringRef(data.data() + previous_offset, offsets[i] - previous_offset) |
54 | 182 | .trim_tail_padding_zero(); |
55 | 182 | } |
56 | | |
57 | 72 | static void get_utf8_char_offsets(const StringRef& ref, Utf8Offsets& offsets) { |
58 | 72 | offsets.clear(); |
59 | 72 | offsets.reserve(ref.size); |
60 | 261 | for (size_t i = 0, char_size = 0; i < ref.size; i += char_size) { |
61 | 189 | char_size = UTF8_BYTE_LENGTH[static_cast<unsigned char>(ref.data[i])]; |
62 | 189 | offsets.push_back(i); |
63 | 189 | } |
64 | 72 | } |
65 | | |
66 | | struct LevenshteinDistance { |
67 | 45 | static Status ascii(const StringRef& left, const StringRef& right, Int32& result) { |
68 | 45 | const StringRef* left_ref = &left; |
69 | 45 | const StringRef* right_ref = &right; |
70 | 45 | size_t m = left.size; |
71 | 45 | size_t n = right.size; |
72 | | |
73 | 45 | if (n > m) { |
74 | 17 | std::swap(left_ref, right_ref); |
75 | 17 | std::swap(m, n); |
76 | 17 | } |
77 | | |
78 | 45 | std::vector<Int32> prev(n + 1); |
79 | 45 | std::vector<Int32> curr(n + 1); |
80 | 187 | for (size_t j = 0; j <= n; ++j) { |
81 | 142 | prev[j] = static_cast<Int32>(j); |
82 | 142 | } |
83 | | |
84 | 222 | for (size_t i = 1; i <= m; ++i) { |
85 | 177 | curr[0] = static_cast<Int32>(i); |
86 | 177 | const char left_char = left_ref->data[i - 1]; |
87 | | |
88 | 674 | for (size_t j = 1; j <= n; ++j) { |
89 | 497 | const Int32 cost = left_char == right_ref->data[j - 1] ? 0 : 1; |
90 | 497 | const Int32 insert_cost = curr[j - 1] + 1; |
91 | 497 | const Int32 delete_cost = prev[j] + 1; |
92 | 497 | const Int32 replace_cost = prev[j - 1] + cost; |
93 | 497 | curr[j] = std::min(std::min(insert_cost, delete_cost), replace_cost); |
94 | 497 | } |
95 | 177 | std::swap(prev, curr); |
96 | 177 | } |
97 | | |
98 | 45 | result = prev[n]; |
99 | 45 | return Status::OK(); |
100 | 45 | } |
101 | | |
102 | | static Status utf8(const StringRef& left, const Utf8Offsets& left_offsets, |
103 | 23 | const StringRef& right, const Utf8Offsets& right_offsets, Int32& result) { |
104 | 23 | const StringRef* left_ref = &left; |
105 | 23 | const StringRef* right_ref = &right; |
106 | 23 | const Utf8Offsets* left_offsets_ref = &left_offsets; |
107 | 23 | const Utf8Offsets* right_offsets_ref = &right_offsets; |
108 | 23 | if (right_offsets_ref->size() > left_offsets_ref->size()) { |
109 | 4 | std::swap(left_offsets_ref, right_offsets_ref); |
110 | 4 | std::swap(left_ref, right_ref); |
111 | 4 | } |
112 | | |
113 | 23 | const size_t m = left_offsets_ref->size(); |
114 | 23 | const size_t n = right_offsets_ref->size(); |
115 | | |
116 | 23 | std::vector<Int32> prev(n + 1); |
117 | 23 | std::vector<Int32> curr(n + 1); |
118 | 102 | for (size_t j = 0; j <= n; ++j) { |
119 | 79 | prev[j] = static_cast<Int32>(j); |
120 | 79 | } |
121 | | |
122 | 90 | for (size_t i = 1; i <= m; ++i) { |
123 | 67 | curr[0] = static_cast<Int32>(i); |
124 | 67 | const size_t left_off = (*left_offsets_ref)[i - 1]; |
125 | 67 | const size_t left_next = i < m ? (*left_offsets_ref)[i] : left_ref->size; |
126 | | |
127 | 235 | for (size_t j = 1; j <= n; ++j) { |
128 | 168 | const size_t right_off = (*right_offsets_ref)[j - 1]; |
129 | 168 | const size_t right_next = j < n ? (*right_offsets_ref)[j] : right_ref->size; |
130 | | |
131 | 168 | const Int32 cost = |
132 | 168 | simd::VStringFunctions::utf8_char_equal(*left_ref, left_off, left_next, |
133 | 168 | *right_ref, right_off, right_next) |
134 | 168 | ? 0 |
135 | 168 | : 1; |
136 | | |
137 | 168 | const Int32 insert_cost = curr[j - 1] + 1; |
138 | 168 | const Int32 delete_cost = prev[j] + 1; |
139 | 168 | const Int32 replace_cost = prev[j - 1] + cost; |
140 | 168 | curr[j] = std::min(std::min(insert_cost, delete_cost), replace_cost); |
141 | 168 | } |
142 | 67 | std::swap(prev, curr); |
143 | 67 | } |
144 | | |
145 | 23 | result = prev[n]; |
146 | 23 | return Status::OK(); |
147 | 23 | } |
148 | | }; |
149 | | |
150 | | struct DamerauLevenshteinDistance { |
151 | | using SymbolId = Int32; |
152 | | using SymbolVector = DorisVector<SymbolId>; |
153 | | using SymbolMap = |
154 | | std::unordered_map<std::string_view, SymbolId, std::hash<std::string_view>, |
155 | | std::equal_to<std::string_view>, |
156 | | CustomStdAllocator<std::pair<const std::string_view, SymbolId>>>; |
157 | | |
158 | | // Do not use absl::strings_internal::CappedDamerauLevenshteinDistance here: |
159 | | // 1. It is capped: distances greater than cutoff return cutoff + 1, not exact distance. |
160 | | // 2. It returns uint8_t and is intended for short strings. |
161 | | // 3. It implements the restricted/OSA variant, not full Damerau-Levenshtein. |
162 | | // 4. It works on bytes, cannot handle UTF-8 characters. |
163 | 20 | static Status ascii(const StringRef& left, const StringRef& right, Int32& result) { |
164 | 20 | const size_t m = left.size; |
165 | 20 | const size_t n = right.size; |
166 | 20 | if (m == 0) { |
167 | 3 | result = static_cast<Int32>(n); |
168 | 3 | return Status::OK(); |
169 | 3 | } |
170 | 17 | if (n == 0) { |
171 | 0 | result = static_cast<Int32>(m); |
172 | 0 | return Status::OK(); |
173 | 0 | } |
174 | | |
175 | 17 | size_t matrix_cells = 0; |
176 | 17 | RETURN_IF_ERROR(get_matrix_cell_count(m, n, matrix_cells)); |
177 | | |
178 | 15 | const Int32 max_distance = static_cast<Int32>(m + n); |
179 | 15 | std::array<Int32, 256> last_row {}; |
180 | 15 | PaddedPODArray<Int32> dist; |
181 | 15 | dist.resize_fill(matrix_cells, 0); |
182 | 1.50k | auto at = [&](size_t i, size_t j) -> Int32& { return dist[i * (n + 2) + j]; }; |
183 | | |
184 | 15 | at(0, 0) = max_distance; |
185 | 84 | for (size_t i = 0; i <= m; ++i) { |
186 | 69 | at(i + 1, 0) = max_distance; |
187 | 69 | at(i + 1, 1) = static_cast<Int32>(i); |
188 | 69 | } |
189 | 90 | for (size_t j = 0; j <= n; ++j) { |
190 | 75 | at(0, j + 1) = max_distance; |
191 | 75 | at(1, j + 1) = static_cast<Int32>(j); |
192 | 75 | } |
193 | | |
194 | 69 | for (size_t i = 1; i <= m; ++i) { |
195 | 54 | Int32 last_match_col = 0; |
196 | 54 | const auto left_symbol = static_cast<unsigned char>(left.data[i - 1]); |
197 | 292 | for (size_t j = 1; j <= n; ++j) { |
198 | 238 | const auto right_symbol = static_cast<unsigned char>(right.data[j - 1]); |
199 | 238 | const Int32 last_match_row = last_row[right_symbol]; |
200 | 238 | const Int32 transposition_col = last_match_col; |
201 | 238 | Int32 cost = 1; |
202 | 238 | if (left_symbol == right_symbol) { |
203 | 50 | cost = 0; |
204 | 50 | last_match_col = static_cast<Int32>(j); |
205 | 50 | } |
206 | | |
207 | 238 | const Int32 replace_cost = at(i, j) + cost; |
208 | 238 | const Int32 insert_cost = at(i + 1, j) + 1; |
209 | 238 | const Int32 delete_cost = at(i, j + 1) + 1; |
210 | 238 | const Int32 transpose_cost = at(last_match_row, transposition_col) + |
211 | 238 | static_cast<Int32>(i - last_match_row - 1) + 1 + |
212 | 238 | static_cast<Int32>(j - transposition_col - 1); |
213 | 238 | at(i + 1, j + 1) = std::min(std::min(replace_cost, insert_cost), |
214 | 238 | std::min(delete_cost, transpose_cost)); |
215 | 238 | } |
216 | 54 | last_row[left_symbol] = static_cast<Int32>(i); |
217 | 54 | } |
218 | | |
219 | 15 | result = at(m + 1, n + 1); |
220 | 15 | return Status::OK(); |
221 | 17 | } |
222 | | |
223 | | /* |
224 | | * Keep the UTF-8 symbol mapping path. The benchmark below shows that mapping each UTF-8 |
225 | | * character to an integer symbol first is faster than comparing UTF-8 slices inside the |
226 | | * dp loop, because the hot loop only needs integer equality and indexed last-row lookup. |
227 | | * |
228 | | * -------------------------------------------------------------------------- |
229 | | * Benchmark Time CPU |
230 | | * -------------------------------------------------------------------------- |
231 | | * BM_DamerauLevenshtein_UTF8/SymbolMapped_16 1.85 us 1.82 us |
232 | | * BM_DamerauLevenshtein_UTF8/DirectCompare_16 6.48 us 6.43 us |
233 | | * BM_DamerauLevenshtein_UTF8/SymbolMapped_64 12.6 us 12.6 us |
234 | | * BM_DamerauLevenshtein_UTF8/DirectCompare_64 119 us 118 us |
235 | | * BM_DamerauLevenshtein_UTF8/SymbolMapped_128 43.4 us 43.1 us |
236 | | * BM_DamerauLevenshtein_UTF8/DirectCompare_128 478 us 475 us |
237 | | * BM_DamerauLevenshtein_UTF8/SymbolMapped_256 160 us 159 us |
238 | | * BM_DamerauLevenshtein_UTF8/DirectCompare_256 1892 us 1882 us |
239 | | */ |
240 | | static Status utf8(const StringRef& left, const Utf8Offsets& left_offsets, |
241 | 9 | const StringRef& right, const Utf8Offsets& right_offsets, Int32& result) { |
242 | 9 | SymbolVector left_symbols; |
243 | 9 | SymbolVector right_symbols; |
244 | 9 | SymbolMap symbol_ids; |
245 | 9 | symbol_ids.reserve(left_offsets.size() + right_offsets.size()); |
246 | 9 | SymbolId next_symbol = 0; |
247 | 9 | append_utf8_symbols(left, left_offsets, left_symbols, symbol_ids, next_symbol); |
248 | 9 | append_utf8_symbols(right, right_offsets, right_symbols, symbol_ids, next_symbol); |
249 | 9 | return by_symbols(left_symbols, right_symbols, next_symbol, result); |
250 | 9 | } |
251 | | |
252 | | private: |
253 | | static Status by_symbols(const SymbolVector& left, const SymbolVector& right, |
254 | 9 | size_t alphabet_size, Int32& result) { |
255 | 9 | const size_t m = left.size(); |
256 | 9 | const size_t n = right.size(); |
257 | 9 | if (m == 0) { |
258 | 0 | result = static_cast<Int32>(n); |
259 | 0 | return Status::OK(); |
260 | 0 | } |
261 | 9 | if (n == 0) { |
262 | 0 | result = static_cast<Int32>(m); |
263 | 0 | return Status::OK(); |
264 | 0 | } |
265 | | |
266 | 9 | size_t matrix_cells = 0; |
267 | 9 | RETURN_IF_ERROR(get_matrix_cell_count(m, n, matrix_cells)); |
268 | | |
269 | 9 | const Int32 max_distance = static_cast<Int32>(m + n); |
270 | 9 | DorisVector<Int32> last_row(alphabet_size, 0); |
271 | 9 | PaddedPODArray<Int32> dist; |
272 | 9 | dist.resize_fill(matrix_cells, 0); |
273 | 441 | auto at = [&](size_t i, size_t j) -> Int32& { return dist[i * (n + 2) + j]; }; |
274 | | |
275 | 9 | at(0, 0) = max_distance; |
276 | 42 | for (size_t i = 0; i <= m; ++i) { |
277 | 33 | at(i + 1, 0) = max_distance; |
278 | 33 | at(i + 1, 1) = static_cast<Int32>(i); |
279 | 33 | } |
280 | 40 | for (size_t j = 0; j <= n; ++j) { |
281 | 31 | at(0, j + 1) = max_distance; |
282 | 31 | at(1, j + 1) = static_cast<Int32>(j); |
283 | 31 | } |
284 | | |
285 | 33 | for (size_t i = 1; i <= m; ++i) { |
286 | 24 | Int32 last_match_col = 0; |
287 | 83 | for (size_t j = 1; j <= n; ++j) { |
288 | 59 | const Int32 last_match_row = last_row[right[j - 1]]; |
289 | 59 | const Int32 transposition_col = last_match_col; |
290 | 59 | Int32 cost = 1; |
291 | 59 | if (left[i - 1] == right[j - 1]) { |
292 | 20 | cost = 0; |
293 | 20 | last_match_col = static_cast<Int32>(j); |
294 | 20 | } |
295 | | |
296 | 59 | const Int32 replace_cost = at(i, j) + cost; |
297 | 59 | const Int32 insert_cost = at(i + 1, j) + 1; |
298 | 59 | const Int32 delete_cost = at(i, j + 1) + 1; |
299 | 59 | const Int32 transpose_cost = at(last_match_row, transposition_col) + |
300 | 59 | static_cast<Int32>(i - last_match_row - 1) + 1 + |
301 | 59 | static_cast<Int32>(j - transposition_col - 1); |
302 | 59 | at(i + 1, j + 1) = std::min(std::min(replace_cost, insert_cost), |
303 | 59 | std::min(delete_cost, transpose_cost)); |
304 | 59 | } |
305 | 24 | last_row[left[i - 1]] = static_cast<Int32>(i); |
306 | 24 | } |
307 | | |
308 | 9 | result = at(m + 1, n + 1); |
309 | 9 | return Status::OK(); |
310 | 9 | } |
311 | | |
312 | 26 | static Status get_matrix_cell_count(size_t m, size_t n, size_t& matrix_cells) { |
313 | 26 | const auto max_size = std::numeric_limits<size_t>::max(); |
314 | 26 | if (m > max_size - 2 || n > max_size - 2) { |
315 | 0 | return Status::InvalidArgument( |
316 | 0 | "damerau_levenshtein_distance input is too large to allocate distance matrix"); |
317 | 0 | } |
318 | | |
319 | 26 | const size_t rows = m + 2; |
320 | 26 | const size_t cols = n + 2; |
321 | 26 | if (rows > max_size / cols) { |
322 | 0 | return Status::InvalidArgument( |
323 | 0 | "damerau_levenshtein_distance distance matrix size overflows"); |
324 | 0 | } |
325 | | |
326 | 26 | matrix_cells = rows * cols; |
327 | 26 | if (matrix_cells > MAX_DAMERAU_LEVENSHTEIN_MATRIX_CELLS) { |
328 | 2 | return Status::InvalidArgument( |
329 | 2 | "damerau_levenshtein_distance distance matrix is too large: {} cells exceeds " |
330 | 2 | "limit {}", |
331 | 2 | matrix_cells, MAX_DAMERAU_LEVENSHTEIN_MATRIX_CELLS); |
332 | 2 | } |
333 | 24 | return Status::OK(); |
334 | 26 | } |
335 | | |
336 | | static void append_utf8_symbols(const StringRef& str, const Utf8Offsets& offsets, |
337 | | SymbolVector& symbols, SymbolMap& symbol_ids, |
338 | 18 | SymbolId& next_symbol) { |
339 | 18 | symbols.reserve(symbols.size() + offsets.size()); |
340 | 64 | for (size_t i = 0; i < offsets.size(); ++i) { |
341 | 46 | const size_t offset = offsets[i]; |
342 | 46 | const size_t next_offset = i + 1 < offsets.size() ? offsets[i + 1] : str.size; |
343 | 46 | const std::string_view symbol(str.data + offset, next_offset - offset); |
344 | 46 | auto [it, inserted] = symbol_ids.emplace(symbol, next_symbol); |
345 | 46 | if (inserted) { |
346 | 26 | ++next_symbol; |
347 | 26 | } |
348 | 46 | symbols.push_back(it->second); |
349 | 46 | } |
350 | 18 | } |
351 | | }; |
352 | | |
353 | | template <typename Distance> |
354 | | struct StringDistanceImplBase { |
355 | | using ResultDataType = DataTypeInt32; |
356 | | using ResultPaddedPODArray = PaddedPODArray<Int32>; |
357 | | |
358 | | static Status vector_vector(const ColumnString::Chars& ldata, |
359 | | const ColumnString::Offsets& loffsets, |
360 | | const ColumnString::Chars& rdata, |
361 | 33 | const ColumnString::Offsets& roffsets, ResultPaddedPODArray& res) { |
362 | 33 | DCHECK_EQ(loffsets.size(), roffsets.size()); |
363 | | |
364 | 33 | const size_t size = loffsets.size(); |
365 | 33 | res.resize(size); |
366 | 33 | Utf8Offsets left_offsets; |
367 | 33 | Utf8Offsets right_offsets; |
368 | 95 | for (size_t i = 0; i < size; ++i) { |
369 | 64 | RETURN_IF_ERROR(distance(string_ref_at(ldata, loffsets, i), |
370 | 64 | string_ref_at(rdata, roffsets, i), left_offsets, right_offsets, |
371 | 64 | res[i])); |
372 | 64 | } |
373 | 31 | return Status::OK(); |
374 | 33 | } _ZN5doris22StringDistanceImplBaseINS_19LevenshteinDistanceEE13vector_vectorERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS3_IjLm4096ES6_Lm16ELm15EEES9_SC_RNS3_IiLm4096ES6_Lm16ELm15EEE Line | Count | Source | 361 | 20 | const ColumnString::Offsets& roffsets, ResultPaddedPODArray& res) { | 362 | 20 | DCHECK_EQ(loffsets.size(), roffsets.size()); | 363 | | | 364 | 20 | const size_t size = loffsets.size(); | 365 | 20 | res.resize(size); | 366 | 20 | Utf8Offsets left_offsets; | 367 | 20 | Utf8Offsets right_offsets; | 368 | 59 | for (size_t i = 0; i < size; ++i) { | 369 | 39 | RETURN_IF_ERROR(distance(string_ref_at(ldata, loffsets, i), | 370 | 39 | string_ref_at(rdata, roffsets, i), left_offsets, right_offsets, | 371 | 39 | res[i])); | 372 | 39 | } | 373 | 20 | return Status::OK(); | 374 | 20 | } |
_ZN5doris22StringDistanceImplBaseINS_26DamerauLevenshteinDistanceEE13vector_vectorERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS3_IjLm4096ES6_Lm16ELm15EEES9_SC_RNS3_IiLm4096ES6_Lm16ELm15EEE Line | Count | Source | 361 | 13 | const ColumnString::Offsets& roffsets, ResultPaddedPODArray& res) { | 362 | 13 | DCHECK_EQ(loffsets.size(), roffsets.size()); | 363 | | | 364 | 13 | const size_t size = loffsets.size(); | 365 | 13 | res.resize(size); | 366 | 13 | Utf8Offsets left_offsets; | 367 | 13 | Utf8Offsets right_offsets; | 368 | 36 | for (size_t i = 0; i < size; ++i) { | 369 | 25 | RETURN_IF_ERROR(distance(string_ref_at(ldata, loffsets, i), | 370 | 25 | string_ref_at(rdata, roffsets, i), left_offsets, right_offsets, | 371 | 25 | res[i])); | 372 | 25 | } | 373 | 11 | return Status::OK(); | 374 | 13 | } |
|
375 | | |
376 | | static Status vector_scalar(const ColumnString::Chars& data, |
377 | | const ColumnString::Offsets& offsets, const StringRef& constant, |
378 | 10 | ResultPaddedPODArray& res) { |
379 | 10 | return vector_const(data, offsets, constant, res); |
380 | 10 | } _ZN5doris22StringDistanceImplBaseINS_19LevenshteinDistanceEE13vector_scalarERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS3_IjLm4096ES6_Lm16ELm15EEERKNS_9StringRefERNS3_IiLm4096ES6_Lm16ELm15EEE Line | Count | Source | 378 | 8 | ResultPaddedPODArray& res) { | 379 | 8 | return vector_const(data, offsets, constant, res); | 380 | 8 | } |
_ZN5doris22StringDistanceImplBaseINS_26DamerauLevenshteinDistanceEE13vector_scalarERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS3_IjLm4096ES6_Lm16ELm15EEERKNS_9StringRefERNS3_IiLm4096ES6_Lm16ELm15EEE Line | Count | Source | 378 | 2 | ResultPaddedPODArray& res) { | 379 | 2 | return vector_const(data, offsets, constant, res); | 380 | 2 | } |
|
381 | | |
382 | | static Status scalar_vector(const StringRef& constant, const ColumnString::Chars& data, |
383 | 10 | const ColumnString::Offsets& offsets, ResultPaddedPODArray& res) { |
384 | 10 | return vector_const(data, offsets, constant, res); |
385 | 10 | } _ZN5doris22StringDistanceImplBaseINS_19LevenshteinDistanceEE13scalar_vectorERKNS_9StringRefERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS6_IjLm4096ES9_Lm16ELm15EEERNS6_IiLm4096ES9_Lm16ELm15EEE Line | Count | Source | 383 | 8 | const ColumnString::Offsets& offsets, ResultPaddedPODArray& res) { | 384 | 8 | return vector_const(data, offsets, constant, res); | 385 | 8 | } |
_ZN5doris22StringDistanceImplBaseINS_26DamerauLevenshteinDistanceEE13scalar_vectorERKNS_9StringRefERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS6_IjLm4096ES9_Lm16ELm15EEERNS6_IiLm4096ES9_Lm16ELm15EEE Line | Count | Source | 383 | 2 | const ColumnString::Offsets& offsets, ResultPaddedPODArray& res) { | 384 | 2 | return vector_const(data, offsets, constant, res); | 385 | 2 | } |
|
386 | | |
387 | | private: |
388 | | static Status vector_const(const ColumnString::Chars& data, |
389 | | const ColumnString::Offsets& offsets, const StringRef& constant, |
390 | 20 | ResultPaddedPODArray& res) { |
391 | 20 | const size_t size = offsets.size(); |
392 | 20 | res.resize(size); |
393 | 20 | const auto constant_ref = constant.trim_tail_padding_zero(); |
394 | 20 | const bool constant_ascii = simd::VStringFunctions::is_ascii(constant_ref); |
395 | 20 | Utf8Offsets constant_offsets; |
396 | 20 | get_utf8_char_offsets(constant_ref, constant_offsets); |
397 | 20 | Utf8Offsets value_offsets; |
398 | 74 | for (size_t i = 0; i < size; ++i) { |
399 | 54 | RETURN_IF_ERROR(distance_with_const_offsets(string_ref_at(data, offsets, i), |
400 | 54 | value_offsets, constant_ref, |
401 | 54 | constant_offsets, constant_ascii, res[i])); |
402 | 54 | } |
403 | 20 | return Status::OK(); |
404 | 20 | } _ZN5doris22StringDistanceImplBaseINS_19LevenshteinDistanceEE12vector_constERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS3_IjLm4096ES6_Lm16ELm15EEERKNS_9StringRefERNS3_IiLm4096ES6_Lm16ELm15EEE Line | Count | Source | 390 | 16 | ResultPaddedPODArray& res) { | 391 | 16 | const size_t size = offsets.size(); | 392 | 16 | res.resize(size); | 393 | 16 | const auto constant_ref = constant.trim_tail_padding_zero(); | 394 | 16 | const bool constant_ascii = simd::VStringFunctions::is_ascii(constant_ref); | 395 | 16 | Utf8Offsets constant_offsets; | 396 | 16 | get_utf8_char_offsets(constant_ref, constant_offsets); | 397 | 16 | Utf8Offsets value_offsets; | 398 | 64 | for (size_t i = 0; i < size; ++i) { | 399 | 48 | RETURN_IF_ERROR(distance_with_const_offsets(string_ref_at(data, offsets, i), | 400 | 48 | value_offsets, constant_ref, | 401 | 48 | constant_offsets, constant_ascii, res[i])); | 402 | 48 | } | 403 | 16 | return Status::OK(); | 404 | 16 | } |
_ZN5doris22StringDistanceImplBaseINS_26DamerauLevenshteinDistanceEE12vector_constERKNS_8PODArrayIhLm4096ENS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEELm16ELm15EEERKNS3_IjLm4096ES6_Lm16ELm15EEERKNS_9StringRefERNS3_IiLm4096ES6_Lm16ELm15EEE Line | Count | Source | 390 | 4 | ResultPaddedPODArray& res) { | 391 | 4 | const size_t size = offsets.size(); | 392 | 4 | res.resize(size); | 393 | 4 | const auto constant_ref = constant.trim_tail_padding_zero(); | 394 | 4 | const bool constant_ascii = simd::VStringFunctions::is_ascii(constant_ref); | 395 | 4 | Utf8Offsets constant_offsets; | 396 | 4 | get_utf8_char_offsets(constant_ref, constant_offsets); | 397 | 4 | Utf8Offsets value_offsets; | 398 | 10 | for (size_t i = 0; i < size; ++i) { | 399 | 6 | RETURN_IF_ERROR(distance_with_const_offsets(string_ref_at(data, offsets, i), | 400 | 6 | value_offsets, constant_ref, | 401 | 6 | constant_offsets, constant_ascii, res[i])); | 402 | 6 | } | 403 | 4 | return Status::OK(); | 404 | 4 | } |
|
405 | | |
406 | | static Status distance(const StringRef& left, const StringRef& right, Utf8Offsets& left_offsets, |
407 | 64 | Utf8Offsets& right_offsets, Int32& result) { |
408 | 64 | const bool left_ascii = simd::VStringFunctions::is_ascii(left); |
409 | 64 | const bool right_ascii = simd::VStringFunctions::is_ascii(right); |
410 | 64 | if (left_ascii && right_ascii) { |
411 | 37 | return Distance::ascii(left, right, result); |
412 | 37 | } |
413 | | |
414 | 27 | if (left.size == 0) { |
415 | 4 | result = static_cast<Int32>( |
416 | 4 | simd::VStringFunctions::get_char_len(right.data, right.size)); |
417 | 4 | return Status::OK(); |
418 | 4 | } |
419 | 23 | if (right.size == 0) { |
420 | 3 | result = static_cast<Int32>(simd::VStringFunctions::get_char_len(left.data, left.size)); |
421 | 3 | return Status::OK(); |
422 | 3 | } |
423 | | |
424 | 20 | get_utf8_char_offsets(left, left_offsets); |
425 | 20 | get_utf8_char_offsets(right, right_offsets); |
426 | 20 | return Distance::utf8(left, left_offsets, right, right_offsets, result); |
427 | 23 | } _ZN5doris22StringDistanceImplBaseINS_19LevenshteinDistanceEE8distanceERKNS_9StringRefES5_RSt6vectorImNS_18CustomStdAllocatorImNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEESD_Ri Line | Count | Source | 407 | 39 | Utf8Offsets& right_offsets, Int32& result) { | 408 | 39 | const bool left_ascii = simd::VStringFunctions::is_ascii(left); | 409 | 39 | const bool right_ascii = simd::VStringFunctions::is_ascii(right); | 410 | 39 | if (left_ascii && right_ascii) { | 411 | 21 | return Distance::ascii(left, right, result); | 412 | 21 | } | 413 | | | 414 | 18 | if (left.size == 0) { | 415 | 3 | result = static_cast<Int32>( | 416 | 3 | simd::VStringFunctions::get_char_len(right.data, right.size)); | 417 | 3 | return Status::OK(); | 418 | 3 | } | 419 | 15 | if (right.size == 0) { | 420 | 2 | result = static_cast<Int32>(simd::VStringFunctions::get_char_len(left.data, left.size)); | 421 | 2 | return Status::OK(); | 422 | 2 | } | 423 | | | 424 | 13 | get_utf8_char_offsets(left, left_offsets); | 425 | 13 | get_utf8_char_offsets(right, right_offsets); | 426 | 13 | return Distance::utf8(left, left_offsets, right, right_offsets, result); | 427 | 15 | } |
_ZN5doris22StringDistanceImplBaseINS_26DamerauLevenshteinDistanceEE8distanceERKNS_9StringRefES5_RSt6vectorImNS_18CustomStdAllocatorImNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEESD_Ri Line | Count | Source | 407 | 25 | Utf8Offsets& right_offsets, Int32& result) { | 408 | 25 | const bool left_ascii = simd::VStringFunctions::is_ascii(left); | 409 | 25 | const bool right_ascii = simd::VStringFunctions::is_ascii(right); | 410 | 25 | if (left_ascii && right_ascii) { | 411 | 16 | return Distance::ascii(left, right, result); | 412 | 16 | } | 413 | | | 414 | 9 | if (left.size == 0) { | 415 | 1 | result = static_cast<Int32>( | 416 | 1 | simd::VStringFunctions::get_char_len(right.data, right.size)); | 417 | 1 | return Status::OK(); | 418 | 1 | } | 419 | 8 | if (right.size == 0) { | 420 | 1 | result = static_cast<Int32>(simd::VStringFunctions::get_char_len(left.data, left.size)); | 421 | 1 | return Status::OK(); | 422 | 1 | } | 423 | | | 424 | 7 | get_utf8_char_offsets(left, left_offsets); | 425 | 7 | get_utf8_char_offsets(right, right_offsets); | 426 | 7 | return Distance::utf8(left, left_offsets, right, right_offsets, result); | 427 | 8 | } |
|
428 | | |
429 | | static Status distance_with_const_offsets(const StringRef& value, Utf8Offsets& value_offsets, |
430 | | const StringRef& constant, |
431 | | const Utf8Offsets& constant_offsets, |
432 | 54 | bool constant_ascii, Int32& result) { |
433 | 54 | const bool value_ascii = simd::VStringFunctions::is_ascii(value); |
434 | 54 | if (value_ascii && constant_ascii) { |
435 | 28 | return Distance::ascii(value, constant, result); |
436 | 28 | } |
437 | | |
438 | 26 | if (value.size == 0) { |
439 | 4 | result = static_cast<Int32>(constant_offsets.size()); |
440 | 4 | return Status::OK(); |
441 | 4 | } |
442 | 22 | if (constant.size == 0) { |
443 | 10 | result = value_ascii ? static_cast<Int32>(value.size) |
444 | 10 | : static_cast<Int32>(simd::VStringFunctions::get_char_len( |
445 | 10 | value.data, value.size)); |
446 | 10 | return Status::OK(); |
447 | 10 | } |
448 | | |
449 | 12 | get_utf8_char_offsets(value, value_offsets); |
450 | 12 | return Distance::utf8(value, value_offsets, constant, constant_offsets, result); |
451 | 22 | } _ZN5doris22StringDistanceImplBaseINS_19LevenshteinDistanceEE27distance_with_const_offsetsERKNS_9StringRefERSt6vectorImNS_18CustomStdAllocatorImNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEES5_RKSC_bRi Line | Count | Source | 432 | 48 | bool constant_ascii, Int32& result) { | 433 | 48 | const bool value_ascii = simd::VStringFunctions::is_ascii(value); | 434 | 48 | if (value_ascii && constant_ascii) { | 435 | 24 | return Distance::ascii(value, constant, result); | 436 | 24 | } | 437 | | | 438 | 24 | if (value.size == 0) { | 439 | 4 | result = static_cast<Int32>(constant_offsets.size()); | 440 | 4 | return Status::OK(); | 441 | 4 | } | 442 | 20 | if (constant.size == 0) { | 443 | 10 | result = value_ascii ? static_cast<Int32>(value.size) | 444 | 10 | : static_cast<Int32>(simd::VStringFunctions::get_char_len( | 445 | 10 | value.data, value.size)); | 446 | 10 | return Status::OK(); | 447 | 10 | } | 448 | | | 449 | 10 | get_utf8_char_offsets(value, value_offsets); | 450 | 10 | return Distance::utf8(value, value_offsets, constant, constant_offsets, result); | 451 | 20 | } |
_ZN5doris22StringDistanceImplBaseINS_26DamerauLevenshteinDistanceEE27distance_with_const_offsetsERKNS_9StringRefERSt6vectorImNS_18CustomStdAllocatorImNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEES5_RKSC_bRi Line | Count | Source | 432 | 6 | bool constant_ascii, Int32& result) { | 433 | 6 | const bool value_ascii = simd::VStringFunctions::is_ascii(value); | 434 | 6 | if (value_ascii && constant_ascii) { | 435 | 4 | return Distance::ascii(value, constant, result); | 436 | 4 | } | 437 | | | 438 | 2 | if (value.size == 0) { | 439 | 0 | result = static_cast<Int32>(constant_offsets.size()); | 440 | 0 | return Status::OK(); | 441 | 0 | } | 442 | 2 | if (constant.size == 0) { | 443 | 0 | result = value_ascii ? static_cast<Int32>(value.size) | 444 | 0 | : static_cast<Int32>(simd::VStringFunctions::get_char_len( | 445 | 0 | value.data, value.size)); | 446 | 0 | return Status::OK(); | 447 | 0 | } | 448 | | | 449 | 2 | get_utf8_char_offsets(value, value_offsets); | 450 | 2 | return Distance::utf8(value, value_offsets, constant, constant_offsets, result); | 451 | 2 | } |
|
452 | | }; |
453 | | |
454 | | template <typename LeftDataType, typename RightDataType> |
455 | | struct LevenshteinImpl : public StringDistanceImplBase<LevenshteinDistance> {}; |
456 | | |
457 | | template <typename LeftDataType, typename RightDataType> |
458 | | struct DamerauLevenshteinDistanceImpl : public StringDistanceImplBase<DamerauLevenshteinDistance> { |
459 | | }; |
460 | | |
461 | | using FunctionLevenshtein = |
462 | | FunctionBinaryToType<DataTypeString, DataTypeString, LevenshteinImpl, NameLevenshtein>; |
463 | | using FunctionDamerauLevenshteinDistance = |
464 | | FunctionBinaryToType<DataTypeString, DataTypeString, DamerauLevenshteinDistanceImpl, |
465 | | NameDamerauLevenshteinDistance>; |
466 | | |
467 | 7 | void register_function_levenshtein(SimpleFunctionFactory& factory) { |
468 | 7 | factory.register_function<FunctionLevenshtein>(); |
469 | 7 | factory.register_alias(FunctionLevenshtein::name, "levenshtein_distance"); |
470 | 7 | factory.register_alias(FunctionLevenshtein::name, "edit_distance"); |
471 | 7 | factory.register_function<FunctionDamerauLevenshteinDistance>(); |
472 | 7 | } |
473 | | |
474 | | } // namespace doris |