fzy 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +19 -0
- data/Gemfile.lock +8 -7
- data/ext/fzy/match.c +139 -82
- data/ext/fzy/match.h +10 -0
- data/fzy.gemspec +2 -2
- data/lib/fzy/version.rb +1 -1
- metadata +15 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a0619dd87bfc5809ad38b3de5c0e6feb9db98da06058ab43f1fe52639cfa6d1
|
4
|
+
data.tar.gz: ac58fc240875d7c0c35e18ef47fc9b8ec27812019845339b8995536c34f3941c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1f0e93c49d10c20fd3a3817fb6ad4b32b4cc4f5b14a7a68cd817e2cf2b4ef4cf21dd728b19be283cbe8cdb57d6a2c129cb349cda0987efce4cdac1563588199
|
7
|
+
data.tar.gz: ef8833e5a05e5d34f45e94b2aaca21db81801a06061ee2d42248b2f25189971df233f4dfefcca11cb8d058da706e65835352e1ec4069b58d5465c5ddae522029
|
@@ -0,0 +1,19 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v1
|
11
|
+
- name: Set up Ruby 2.6
|
12
|
+
uses: actions/setup-ruby@v1
|
13
|
+
with:
|
14
|
+
ruby-version: 2.6.x
|
15
|
+
- name: Build and test with Rake
|
16
|
+
run: |
|
17
|
+
gem install bundler
|
18
|
+
bundle install --jobs 4 --retry 3
|
19
|
+
bundle exec rake
|
data/Gemfile.lock
CHANGED
@@ -1,25 +1,26 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
fzy (0.
|
4
|
+
fzy (0.2.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
minitest (5.
|
10
|
-
rake (
|
11
|
-
rake-compiler (1.
|
9
|
+
minitest (5.25.4)
|
10
|
+
rake (13.2.1)
|
11
|
+
rake-compiler (1.2.8)
|
12
12
|
rake
|
13
13
|
|
14
14
|
PLATFORMS
|
15
|
+
arm64-darwin-23
|
15
16
|
ruby
|
16
17
|
|
17
18
|
DEPENDENCIES
|
18
|
-
bundler
|
19
|
+
bundler
|
19
20
|
fzy!
|
20
21
|
minitest
|
21
|
-
rake
|
22
|
+
rake
|
22
23
|
rake-compiler
|
23
24
|
|
24
25
|
BUNDLED WITH
|
25
|
-
|
26
|
+
2.5.17
|
data/ext/fzy/match.c
CHANGED
@@ -4,6 +4,7 @@
|
|
4
4
|
#include <stdio.h>
|
5
5
|
#include <float.h>
|
6
6
|
#include <math.h>
|
7
|
+
#include <stdlib.h>
|
7
8
|
|
8
9
|
#include "match.h"
|
9
10
|
#include "bonus.h"
|
@@ -27,122 +28,177 @@ int has_match(const char *needle, const char *haystack) {
|
|
27
28
|
return 1;
|
28
29
|
}
|
29
30
|
|
31
|
+
#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0)
|
32
|
+
|
30
33
|
#define max(a, b) (((a) > (b)) ? (a) : (b))
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
}
|
42
|
-
fprintf(stderr, "\n");
|
43
|
-
for (i = 0; i < n; i++) {
|
44
|
-
fprintf(stderr, " %c |", needle[i]);
|
45
|
-
for (j = 0; j < m; j++) {
|
46
|
-
score_t val = mat[i * m + j];
|
47
|
-
if (val == SCORE_MIN) {
|
48
|
-
fprintf(stderr, " -\u221E");
|
49
|
-
} else {
|
50
|
-
fprintf(stderr, " %.3f", val);
|
51
|
-
}
|
52
|
-
}
|
53
|
-
fprintf(stderr, "\n");
|
54
|
-
}
|
55
|
-
fprintf(stderr, "\n\n");
|
56
|
-
}
|
57
|
-
#endif
|
35
|
+
struct match_struct {
|
36
|
+
int needle_len;
|
37
|
+
int haystack_len;
|
38
|
+
|
39
|
+
char lower_needle[MATCH_MAX_LEN];
|
40
|
+
char lower_haystack[MATCH_MAX_LEN];
|
41
|
+
|
42
|
+
score_t match_bonus[MATCH_MAX_LEN];
|
43
|
+
};
|
58
44
|
|
59
45
|
static void precompute_bonus(const char *haystack, score_t *match_bonus) {
|
60
46
|
/* Which positions are beginning of words */
|
61
|
-
int m = strlen(haystack);
|
62
47
|
char last_ch = '/';
|
63
|
-
for (int i = 0; i
|
48
|
+
for (int i = 0; haystack[i]; i++) {
|
64
49
|
char ch = haystack[i];
|
65
50
|
match_bonus[i] = COMPUTE_BONUS(last_ch, ch);
|
66
51
|
last_ch = ch;
|
67
52
|
}
|
68
53
|
}
|
69
54
|
|
70
|
-
|
55
|
+
static void setup_match_struct(struct match_struct *match, const char *needle, const char *haystack) {
|
56
|
+
match->needle_len = strlen(needle);
|
57
|
+
match->haystack_len = strlen(haystack);
|
58
|
+
|
59
|
+
if (match->haystack_len > MATCH_MAX_LEN || match->needle_len > match->haystack_len) {
|
60
|
+
return;
|
61
|
+
}
|
62
|
+
|
63
|
+
for (int i = 0; i < match->needle_len; i++)
|
64
|
+
match->lower_needle[i] = tolower(needle[i]);
|
65
|
+
|
66
|
+
for (int i = 0; i < match->haystack_len; i++)
|
67
|
+
match->lower_haystack[i] = tolower(haystack[i]);
|
68
|
+
|
69
|
+
precompute_bonus(haystack, match->match_bonus);
|
70
|
+
}
|
71
|
+
|
72
|
+
static inline void match_row(const struct match_struct *match, int row, score_t *curr_D, score_t *curr_M, const score_t *last_D, const score_t *last_M) {
|
73
|
+
int n = match->needle_len;
|
74
|
+
int m = match->haystack_len;
|
75
|
+
int i = row;
|
76
|
+
|
77
|
+
const char *lower_needle = match->lower_needle;
|
78
|
+
const char *lower_haystack = match->lower_haystack;
|
79
|
+
const score_t *match_bonus = match->match_bonus;
|
80
|
+
|
81
|
+
score_t prev_score = SCORE_MIN;
|
82
|
+
score_t gap_score = i == n - 1 ? SCORE_GAP_TRAILING : SCORE_GAP_INNER;
|
83
|
+
|
84
|
+
for (int j = 0; j < m; j++) {
|
85
|
+
if (lower_needle[i] == lower_haystack[j]) {
|
86
|
+
score_t score = SCORE_MIN;
|
87
|
+
if (!i) {
|
88
|
+
score = (j * SCORE_GAP_LEADING) + match_bonus[j];
|
89
|
+
} else if (j) { /* i > 0 && j > 0*/
|
90
|
+
score = max(
|
91
|
+
last_M[j - 1] + match_bonus[j],
|
92
|
+
|
93
|
+
/* consecutive match, doesn't stack with match_bonus */
|
94
|
+
last_D[j - 1] + SCORE_MATCH_CONSECUTIVE);
|
95
|
+
}
|
96
|
+
curr_D[j] = score;
|
97
|
+
curr_M[j] = prev_score = max(score, prev_score + gap_score);
|
98
|
+
} else {
|
99
|
+
curr_D[j] = SCORE_MIN;
|
100
|
+
curr_M[j] = prev_score = prev_score + gap_score;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
score_t match(const char *needle, const char *haystack) {
|
71
106
|
if (!*needle)
|
72
107
|
return SCORE_MIN;
|
73
108
|
|
74
|
-
|
75
|
-
|
109
|
+
struct match_struct match;
|
110
|
+
setup_match_struct(&match, needle, haystack);
|
76
111
|
|
77
|
-
|
112
|
+
int n = match.needle_len;
|
113
|
+
int m = match.haystack_len;
|
114
|
+
|
115
|
+
if (m > MATCH_MAX_LEN || n > m) {
|
116
|
+
/*
|
117
|
+
* Unreasonably large candidate: return no score
|
118
|
+
* If it is a valid match it will still be returned, it will
|
119
|
+
* just be ranked below any reasonably sized candidates
|
120
|
+
*/
|
121
|
+
return SCORE_MIN;
|
122
|
+
} else if (n == m) {
|
78
123
|
/* Since this method can only be called with a haystack which
|
79
124
|
* matches needle. If the lengths of the strings are equal the
|
80
125
|
* strings themselves must also be equal (ignoring case).
|
81
126
|
*/
|
82
|
-
if (positions)
|
83
|
-
for (int i = 0; i < n; i++)
|
84
|
-
positions[i] = i;
|
85
127
|
return SCORE_MAX;
|
86
128
|
}
|
87
129
|
|
88
|
-
|
130
|
+
/*
|
131
|
+
* D[][] Stores the best score for this position ending with a match.
|
132
|
+
* M[][] Stores the best possible score at this position.
|
133
|
+
*/
|
134
|
+
score_t D[2][MATCH_MAX_LEN], M[2][MATCH_MAX_LEN];
|
135
|
+
|
136
|
+
score_t *last_D, *last_M;
|
137
|
+
score_t *curr_D, *curr_M;
|
138
|
+
|
139
|
+
last_D = D[0];
|
140
|
+
last_M = M[0];
|
141
|
+
curr_D = D[1];
|
142
|
+
curr_M = M[1];
|
143
|
+
|
144
|
+
for (int i = 0; i < n; i++) {
|
145
|
+
match_row(&match, i, curr_D, curr_M, last_D, last_M);
|
146
|
+
|
147
|
+
SWAP(curr_D, last_D, score_t *);
|
148
|
+
SWAP(curr_M, last_M, score_t *);
|
149
|
+
}
|
150
|
+
|
151
|
+
return last_M[m - 1];
|
152
|
+
}
|
153
|
+
|
154
|
+
score_t match_positions(const char *needle, const char *haystack, size_t *positions) {
|
155
|
+
if (!*needle)
|
156
|
+
return SCORE_MIN;
|
157
|
+
|
158
|
+
struct match_struct match;
|
159
|
+
setup_match_struct(&match, needle, haystack);
|
160
|
+
|
161
|
+
int n = match.needle_len;
|
162
|
+
int m = match.haystack_len;
|
163
|
+
|
164
|
+
if (m > MATCH_MAX_LEN || n > m) {
|
89
165
|
/*
|
90
166
|
* Unreasonably large candidate: return no score
|
91
167
|
* If it is a valid match it will still be returned, it will
|
92
168
|
* just be ranked below any reasonably sized candidates
|
93
169
|
*/
|
94
170
|
return SCORE_MIN;
|
171
|
+
} else if (n == m) {
|
172
|
+
/* Since this method can only be called with a haystack which
|
173
|
+
* matches needle. If the lengths of the strings are equal the
|
174
|
+
* strings themselves must also be equal (ignoring case).
|
175
|
+
*/
|
176
|
+
if (positions)
|
177
|
+
for (int i = 0; i < n; i++)
|
178
|
+
positions[i] = i;
|
179
|
+
return SCORE_MAX;
|
95
180
|
}
|
96
181
|
|
97
|
-
char lower_needle[n];
|
98
|
-
char lower_haystack[m];
|
99
|
-
|
100
|
-
for (int i = 0; i < n; i++)
|
101
|
-
lower_needle[i] = tolower(needle[i]);
|
102
|
-
|
103
|
-
for (int i = 0; i < m; i++)
|
104
|
-
lower_haystack[i] = tolower(haystack[i]);
|
105
|
-
|
106
|
-
score_t match_bonus[m];
|
107
|
-
score_t D[n][m], M[n][m];
|
108
|
-
|
109
182
|
/*
|
110
183
|
* D[][] Stores the best score for this position ending with a match.
|
111
184
|
* M[][] Stores the best possible score at this position.
|
112
185
|
*/
|
113
|
-
|
186
|
+
score_t (*D)[MATCH_MAX_LEN], (*M)[MATCH_MAX_LEN];
|
187
|
+
M = malloc(sizeof(score_t) * MATCH_MAX_LEN * n);
|
188
|
+
D = malloc(sizeof(score_t) * MATCH_MAX_LEN * n);
|
189
|
+
|
190
|
+
score_t *last_D = NULL, *last_M = NULL;
|
191
|
+
score_t *curr_D, *curr_M;
|
114
192
|
|
115
193
|
for (int i = 0; i < n; i++) {
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
for (int j = 0; j < m; j++) {
|
120
|
-
if (lower_needle[i] == lower_haystack[j]) {
|
121
|
-
score_t score = SCORE_MIN;
|
122
|
-
if (!i) {
|
123
|
-
score = (j * SCORE_GAP_LEADING) + match_bonus[j];
|
124
|
-
} else if (j) { /* i > 0 && j > 0*/
|
125
|
-
score = max(
|
126
|
-
M[i - 1][j - 1] + match_bonus[j],
|
127
|
-
|
128
|
-
/* consecutive match, doesn't stack with match_bonus */
|
129
|
-
D[i - 1][j - 1] + SCORE_MATCH_CONSECUTIVE);
|
130
|
-
}
|
131
|
-
D[i][j] = score;
|
132
|
-
M[i][j] = prev_score = max(score, prev_score + gap_score);
|
133
|
-
} else {
|
134
|
-
D[i][j] = SCORE_MIN;
|
135
|
-
M[i][j] = prev_score = prev_score + gap_score;
|
136
|
-
}
|
137
|
-
}
|
138
|
-
}
|
194
|
+
curr_D = &D[i][0];
|
195
|
+
curr_M = &M[i][0];
|
139
196
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
#endif
|
197
|
+
match_row(&match, i, curr_D, curr_M, last_D, last_M);
|
198
|
+
|
199
|
+
last_D = curr_D;
|
200
|
+
last_M = curr_M;
|
201
|
+
}
|
146
202
|
|
147
203
|
/* backtrace to find the positions of optimal matching */
|
148
204
|
if (positions) {
|
@@ -173,9 +229,10 @@ score_t match_positions(const char *needle, const char *haystack, size_t *positi
|
|
173
229
|
}
|
174
230
|
}
|
175
231
|
|
176
|
-
|
177
|
-
}
|
232
|
+
score_t result = M[n - 1][m - 1];
|
178
233
|
|
179
|
-
|
180
|
-
|
234
|
+
free(M);
|
235
|
+
free(D);
|
236
|
+
|
237
|
+
return result;
|
181
238
|
}
|
data/ext/fzy/match.h
CHANGED
@@ -3,12 +3,22 @@
|
|
3
3
|
|
4
4
|
#include <math.h>
|
5
5
|
|
6
|
+
#ifdef __cplusplus
|
7
|
+
extern "C" {
|
8
|
+
#endif
|
9
|
+
|
6
10
|
typedef double score_t;
|
7
11
|
#define SCORE_MAX INFINITY
|
8
12
|
#define SCORE_MIN -INFINITY
|
9
13
|
|
14
|
+
#define MATCH_MAX_LEN 1024
|
15
|
+
|
10
16
|
int has_match(const char *needle, const char *haystack);
|
11
17
|
score_t match_positions(const char *needle, const char *haystack, size_t *positions);
|
12
18
|
score_t match(const char *needle, const char *haystack);
|
13
19
|
|
20
|
+
#ifdef __cplusplus
|
21
|
+
}
|
22
|
+
#endif
|
23
|
+
|
14
24
|
#endif
|
data/fzy.gemspec
CHANGED
@@ -24,8 +24,8 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
spec.extensions = ["ext/fzy/extconf.rb"]
|
26
26
|
|
27
|
-
spec.add_development_dependency "bundler"
|
28
|
-
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "bundler"
|
28
|
+
spec.add_development_dependency "rake"
|
29
29
|
spec.add_development_dependency "rake-compiler"
|
30
30
|
spec.add_development_dependency "minitest"
|
31
31
|
end
|
data/lib/fzy/version.rb
CHANGED
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fzy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Hawthorn
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake-compiler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,6 +74,7 @@ extensions:
|
|
74
74
|
- ext/fzy/extconf.rb
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- ".github/workflows/ci.yml"
|
77
78
|
- ".gitignore"
|
78
79
|
- CODE_OF_CONDUCT.md
|
79
80
|
- Gemfile
|
@@ -97,7 +98,7 @@ homepage: https://github.com/jhawthorn/fzy.js
|
|
97
98
|
licenses:
|
98
99
|
- MIT
|
99
100
|
metadata: {}
|
100
|
-
post_install_message:
|
101
|
+
post_install_message:
|
101
102
|
rdoc_options: []
|
102
103
|
require_paths:
|
103
104
|
- lib
|
@@ -112,8 +113,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
113
|
- !ruby/object:Gem::Version
|
113
114
|
version: '0'
|
114
115
|
requirements: []
|
115
|
-
rubygems_version: 3.
|
116
|
-
signing_key:
|
116
|
+
rubygems_version: 3.5.16
|
117
|
+
signing_key:
|
117
118
|
specification_version: 4
|
118
119
|
summary: A better fuzzy finder
|
119
120
|
test_files: []
|