Coverage Report

Created: 2026-07-08 14:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
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