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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d9b0ca699907c027e4cc3196971d3bb6cadd90e84d7cbb3e715ab6646560c0f
4
- data.tar.gz: 52efaf9f44e76b5c15cd71e2b7b26aaa7364e290de28d0658bf149e282bed2d3
3
+ metadata.gz: 2a0619dd87bfc5809ad38b3de5c0e6feb9db98da06058ab43f1fe52639cfa6d1
4
+ data.tar.gz: ac58fc240875d7c0c35e18ef47fc9b8ec27812019845339b8995536c34f3941c
5
5
  SHA512:
6
- metadata.gz: 6199c4ec774da59b321ca878d7bc60cd9109fd3ceb7b56b785a74e6ba43471dd65a972bb09705e93dc7612d750e2363171265cb4c57bde4d0b9db5ce2a5359db
7
- data.tar.gz: bf7d4f1f30ad6fe4050dfed9407fc8e3ebb08f11e4d190dab69476c714819bd4212d9798b32f720a1ef9f1a6f50a418a8e73304d33441627d59a6aa6e49e530b
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.1.0)
4
+ fzy (0.2.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
- minitest (5.13.0)
10
- rake (10.5.0)
11
- rake-compiler (1.0.8)
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 (~> 1.17)
19
+ bundler
19
20
  fzy!
20
21
  minitest
21
- rake (~> 10.0)
22
+ rake
22
23
  rake-compiler
23
24
 
24
25
  BUNDLED WITH
25
- 1.17.3
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
- #ifdef DEBUG_VERBOSE
33
- /* print one of the internal matrices */
34
- void mat_print(score_t *mat, char name, const char *needle, const char *haystack) {
35
- int n = strlen(needle);
36
- int m = strlen(haystack);
37
- int i, j;
38
- fprintf(stderr, "%c ", name);
39
- for (j = 0; j < m; j++) {
40
- fprintf(stderr, " %c", haystack[j]);
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 < m; 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
- score_t match_positions(const char *needle, const char *haystack, size_t *positions) {
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
- int n = strlen(needle);
75
- int m = strlen(haystack);
109
+ struct match_struct match;
110
+ setup_match_struct(&match, needle, haystack);
76
111
 
77
- if (n == m) {
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
- if (m > 1024) {
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
- precompute_bonus(haystack, match_bonus);
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
- score_t prev_score = SCORE_MIN;
117
- score_t gap_score = i == n - 1 ? SCORE_GAP_TRAILING : SCORE_GAP_INNER;
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
- #ifdef DEBUG_VERBOSE
141
- fprintf(stderr, "\"%s\" =~ \"%s\"\n", needle, haystack);
142
- mat_print(&D[0][0], 'D', needle, haystack);
143
- mat_print(&M[0][0], 'M', needle, haystack);
144
- fprintf(stderr, "\n");
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
- return M[n - 1][m - 1];
177
- }
232
+ score_t result = M[n - 1][m - 1];
178
233
 
179
- score_t match(const char *needle, const char *haystack) {
180
- return match_positions(needle, haystack, NULL);
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", "~> 1.17"
28
- spec.add_development_dependency "rake", "~> 10.0"
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
@@ -1,3 +1,3 @@
1
1
  module Fzy
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
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.1.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: 2019-11-09 00:00:00.000000000 Z
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: '1.17'
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: '1.17'
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: '10.0'
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: '10.0'
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.0.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: []