/src/php-src/ext/standard/levenshtein.c
Line | Count | Source |
1 | | /* |
2 | | +----------------------------------------------------------------------+ |
3 | | | Copyright © The PHP Group and Contributors. | |
4 | | +----------------------------------------------------------------------+ |
5 | | | This source file is subject to the Modified BSD License that is | |
6 | | | bundled with this package in the file LICENSE, and is available | |
7 | | | through the World Wide Web at <https://www.php.net/license/>. | |
8 | | | | |
9 | | | SPDX-License-Identifier: BSD-3-Clause | |
10 | | +----------------------------------------------------------------------+ |
11 | | | Author: Hartmut Holzgraefe <hholzgra@php.net> | |
12 | | +----------------------------------------------------------------------+ |
13 | | */ |
14 | | |
15 | | #include "php.h" |
16 | | |
17 | | /* {{{ reference_levdist |
18 | | * reference implementation, only optimized for memory usage, not speed */ |
19 | | static bool reference_levdist(const zend_string *string1, const zend_string *string2, zend_long cost_ins, zend_long cost_rep, zend_long cost_del, zend_long *result) |
20 | 0 | { |
21 | 0 | zend_long *p1, *p2, *tmp; |
22 | 0 | zend_long c0, c1, c2; |
23 | 0 | size_t i1, i2; |
24 | |
|
25 | 0 | if (ZSTR_LEN(string1) == 0) { |
26 | 0 | *result = ZSTR_LEN(string2) * cost_ins; |
27 | 0 | return true; |
28 | 0 | } |
29 | 0 | if (ZSTR_LEN(string2) == 0) { |
30 | 0 | *result = ZSTR_LEN(string1) * cost_del; |
31 | 0 | return true; |
32 | 0 | } |
33 | | |
34 | | /* When all costs are equal, levenshtein fulfills the requirements of a metric, which means |
35 | | * that the distance is symmetric. If string1 is shorter than string 2 we can save memory (and CPU time) |
36 | | * by having shorter rows (p1 & p2). */ |
37 | 0 | if (ZSTR_LEN(string1) < ZSTR_LEN(string2) && cost_ins == cost_rep && cost_rep == cost_del) { |
38 | 0 | const zend_string *tmp = string1; |
39 | 0 | string1 = string2; |
40 | 0 | string2 = tmp; |
41 | 0 | } |
42 | | |
43 | | /* Each row is eight times the length of string2. */ |
44 | 0 | p1 = safe_femalloc((ZSTR_LEN(string2) + 1), sizeof(zend_long), 0, /* fallible */ true); |
45 | 0 | if (UNEXPECTED(!p1)) { |
46 | 0 | return false; |
47 | 0 | } |
48 | 0 | p2 = safe_femalloc((ZSTR_LEN(string2) + 1), sizeof(zend_long), 0, /* fallible */ true); |
49 | 0 | if (UNEXPECTED(!p2)) { |
50 | 0 | efree(p1); |
51 | 0 | return false; |
52 | 0 | } |
53 | | |
54 | 0 | for (i2 = 0; i2 <= ZSTR_LEN(string2); i2++) { |
55 | 0 | p1[i2] = i2 * cost_ins; |
56 | 0 | } |
57 | 0 | for (i1 = 0; i1 < ZSTR_LEN(string1) ; i1++) { |
58 | 0 | p2[0] = p1[0] + cost_del; |
59 | |
|
60 | 0 | for (i2 = 0; i2 < ZSTR_LEN(string2); i2++) { |
61 | 0 | c0 = p1[i2] + ((ZSTR_VAL(string1)[i1] == ZSTR_VAL(string2)[i2]) ? 0 : cost_rep); |
62 | 0 | c1 = p1[i2 + 1] + cost_del; |
63 | 0 | if (c1 < c0) { |
64 | 0 | c0 = c1; |
65 | 0 | } |
66 | 0 | c2 = p2[i2] + cost_ins; |
67 | 0 | if (c2 < c0) { |
68 | 0 | c0 = c2; |
69 | 0 | } |
70 | 0 | p2[i2 + 1] = c0; |
71 | 0 | } |
72 | 0 | tmp = p1; |
73 | 0 | p1 = p2; |
74 | 0 | p2 = tmp; |
75 | 0 | } |
76 | 0 | c0 = p1[ZSTR_LEN(string2)]; |
77 | |
|
78 | 0 | efree(p1); |
79 | 0 | efree(p2); |
80 | |
|
81 | 0 | *result = c0; |
82 | 0 | return true; |
83 | 0 | } |
84 | | /* }}} */ |
85 | | |
86 | | /* {{{ Calculate Levenshtein distance between two strings */ |
87 | | PHP_FUNCTION(levenshtein) |
88 | 0 | { |
89 | 0 | zend_string *string1, *string2; |
90 | 0 | zend_long cost_ins = 1; |
91 | 0 | zend_long cost_rep = 1; |
92 | 0 | zend_long cost_del = 1; |
93 | |
|
94 | 0 | ZEND_PARSE_PARAMETERS_START(2, 5) |
95 | 0 | Z_PARAM_STR(string1) |
96 | 0 | Z_PARAM_STR(string2) |
97 | 0 | Z_PARAM_OPTIONAL |
98 | 0 | Z_PARAM_LONG(cost_ins) |
99 | 0 | Z_PARAM_LONG(cost_rep) |
100 | 0 | Z_PARAM_LONG(cost_del) |
101 | 0 | ZEND_PARSE_PARAMETERS_END(); |
102 | | |
103 | 0 | zend_long distance; |
104 | 0 | if (UNEXPECTED(!reference_levdist(string1, string2, cost_ins, cost_rep, cost_del, &distance))) { |
105 | 0 | RETURN_OOM(); |
106 | 0 | } |
107 | 0 | RETURN_LONG(distance); |
108 | 0 | } |
109 | | /* }}} */ |