mittens 0.3.2 → 0.3.4

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: b5d5216e3e6a0e86aa92ab80ec8876e66be4d766d500d82609f166452f28fb08
4
- data.tar.gz: f5db98b8a25cc9fb2183a2f0010695239a66c82faf00e1ddb34355739087c48b
3
+ metadata.gz: d7e35e680454076f43e5973db399645697e0e98732555293d6b5e3bae115308d
4
+ data.tar.gz: 6c619fe9518ba5ddd42001ee0e99b4883b0fa703d5bf7b45e9022a039c78fb7d
5
5
  SHA512:
6
- metadata.gz: 886dac65964a1b37c5156ff27b7703a71d6acde7f01c9525d1a564636b1fdfa99bf8871b98e8628ed511cdda536e4d28d7e5bebbefdb9b0a67454346e1f571c7
7
- data.tar.gz: 62a9a20fca62714a28826626183dba6816d11863fb169da43c0fa7d35d2a7978704f4764be63bce5de1986b188566f15f7ec5be5a0d415d9a85fad7285b2f1ac
6
+ metadata.gz: 1148ef35cf15d3027b51934844763437f1a606b2eed92dec9ccb17c86a78e544d664a51e6470da7d117c3a30fb1e0638119f3f1091fb203b29630e11513d5672
7
+ data.tar.gz: c1cc877ce40bf0c2fc3dd9a7946b4cf0165f870392ff87024c3a717cfe742420b8de6b1bbe8ecf5645e0566f81ed906f1ce87a9072ba645c4ca3096702b253d8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.3.4 (2026-08-06)
2
+
3
+ - Fixed `dup` and `clone` methods
4
+
5
+ ## 0.3.3 (2026-08-05)
6
+
7
+ - Fixed issue with GC compaction
8
+
1
9
  ## 0.3.2 (2026-07-10)
2
10
 
3
11
  - Updated Snowball to 3.1.1
data/ext/mittens/ext.c CHANGED
@@ -1,21 +1,37 @@
1
+ #include <limits.h>
2
+
1
3
  #include "libstemmer.h"
2
4
  #include "ruby/ruby.h"
3
5
 
6
+ #define GetAlgorithm(language) (NIL_P(language) ? "english" : StringValueCStr(language))
7
+ #define GetStemmer(obj, ptr) TypedData_Get_Struct((obj), stemmer_t, &stemmer_data_type, (ptr))
8
+
4
9
  typedef struct stemmer {
5
- struct sb_stemmer * stemmer;
10
+ struct sb_stemmer* stemmer;
11
+ VALUE language;
6
12
  } stemmer_t;
7
13
 
8
- static void stemmer_free(void *ptr)
14
+ static void stemmer_mark(void* ptr)
9
15
  {
10
- stemmer_t *stemmer = (stemmer_t *)ptr;
11
- // safe to pass null pointer according to docs
12
- sb_stemmer_delete(stemmer->stemmer);
16
+ stemmer_t* stemmer = (stemmer_t*) ptr;
17
+ rb_gc_mark(stemmer->language);
18
+ }
19
+
20
+ static void stemmer_free(void* ptr)
21
+ {
22
+ stemmer_t* stemmer = (stemmer_t*) ptr;
23
+
24
+ // safe to pass null pointer, but check anyways
25
+ if (stemmer->stemmer != NULL)
26
+ sb_stemmer_delete(stemmer->stemmer);
27
+
13
28
  xfree(ptr);
14
29
  }
15
30
 
16
31
  const rb_data_type_t stemmer_data_type = {
17
- .wrap_struct_name = "stemmer",
32
+ .wrap_struct_name = "Mittens::Stemmer",
18
33
  .function = {
34
+ .dmark = stemmer_mark,
19
35
  .dfree = stemmer_free,
20
36
  },
21
37
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
@@ -23,74 +39,104 @@ const rb_data_type_t stemmer_data_type = {
23
39
 
24
40
  static VALUE stemmer_allocate(VALUE klass)
25
41
  {
26
- stemmer_t *stemmer;
42
+ stemmer_t* stemmer;
27
43
  VALUE obj = TypedData_Make_Struct(klass, stemmer_t, &stemmer_data_type, stemmer);
28
44
  stemmer->stemmer = NULL;
45
+ stemmer->language = Qnil;
29
46
  return obj;
30
47
  }
31
48
 
32
49
  static VALUE stemmer_initialize(int argc, VALUE* argv, VALUE self)
33
50
  {
34
- VALUE opts;
35
- rb_scan_args(argc, argv, ":", &opts);
51
+ stemmer_t* stemmer;
52
+ GetStemmer(self, stemmer);
36
53
 
37
- const char * algorithm = "english";
38
- if (!NIL_P(opts)) {
39
- VALUE language = rb_hash_aref(opts, ID2SYM(rb_intern("language")));
40
- if (!NIL_P(language)) {
41
- Check_Type(language, T_STRING);
42
- algorithm = RSTRING_PTR(language);
43
- }
44
- }
54
+ if (stemmer->stemmer != NULL)
55
+ rb_raise(rb_eRuntimeError, "stemmer already initialized");
45
56
 
46
- stemmer_t *stemmer;
47
- TypedData_Get_Struct(self, stemmer_t, &stemmer_data_type, stemmer);
57
+ VALUE opts;
58
+ rb_scan_args(argc, argv, ":", &opts);
48
59
 
49
- // in case called multiple times
50
- sb_stemmer_delete(stemmer->stemmer);
60
+ VALUE language = Qnil;
61
+ if (!NIL_P(opts))
62
+ language = rb_hash_aref(opts, ID2SYM(rb_intern("language")));
51
63
 
52
64
  // if adding support for encoding, may want to change encoding returned from stem
53
- stemmer->stemmer = sb_stemmer_new(algorithm, NULL);
54
- if (stemmer->stemmer == NULL) {
55
- rb_raise(rb_eArgError, "unknown language: %s", algorithm);
65
+ stemmer->stemmer = sb_stemmer_new(GetAlgorithm(language), NULL);
66
+ if (stemmer->stemmer == NULL)
67
+ {
68
+ if (!NIL_P(language))
69
+ rb_raise(rb_eArgError, "unknown language: %" PRIsVALUE, language);
70
+
71
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
56
72
  }
57
73
 
74
+ // copy (after GetAlgorithm) in case string is modified in future
75
+ stemmer->language = rb_obj_dup(language);
76
+
58
77
  return self;
59
78
  }
60
79
 
61
80
  static VALUE stemmer_stem(VALUE self, VALUE value)
62
81
  {
63
- stemmer_t *stemmer;
64
- TypedData_Get_Struct(self, stemmer_t, &stemmer_data_type, stemmer);
82
+ stemmer_t* stemmer;
83
+ GetStemmer(self, stemmer);
84
+
85
+ if (stemmer->stemmer == NULL)
86
+ rb_raise(rb_eRuntimeError, "stemmer not initialized");
65
87
 
66
- Check_Type(value, T_STRING);
88
+ const char* word = StringValuePtr(value);
89
+ long size = RSTRING_LEN(value);
90
+ if (size > INT_MAX)
91
+ rb_raise(rb_eArgError, "string exceeds max length");
67
92
 
68
- const sb_symbol * word = (const sb_symbol *) RSTRING_PTR(value);
69
- int size = (int) RSTRING_LEN(value);
70
- const sb_symbol * pointer_out = sb_stemmer_stem(stemmer->stemmer, word, size);
93
+ const sb_symbol* pointer_out = sb_stemmer_stem(stemmer->stemmer, (const sb_symbol*) word, (int) size);
94
+ int length_out = sb_stemmer_length(stemmer->stemmer);
71
95
 
72
- return rb_utf8_str_new_cstr((char *) pointer_out);
96
+ // place after last use of word
97
+ RB_GC_GUARD(value);
98
+
99
+ return rb_utf8_str_new((char*) pointer_out, (long) length_out);
100
+ }
101
+
102
+ static VALUE stemmer_initialize_copy(VALUE self, VALUE orig)
103
+ {
104
+ stemmer_t* stemmer;
105
+ GetStemmer(self, stemmer);
106
+
107
+ stemmer_t* orig_stemmer;
108
+ GetStemmer(orig, orig_stemmer);
109
+
110
+ if (stemmer->stemmer != NULL)
111
+ rb_raise(rb_eRuntimeError, "stemmer already initialized");
112
+
113
+ stemmer->stemmer = sb_stemmer_new(GetAlgorithm(orig_stemmer->language), NULL);
114
+ if (stemmer->stemmer == NULL)
115
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
116
+
117
+ stemmer->language = orig_stemmer->language;
118
+
119
+ return self;
73
120
  }
74
121
 
75
122
  static VALUE stemmer_languages(VALUE klass)
76
123
  {
77
124
  VALUE out = rb_ary_new();
78
125
 
79
- const char **language = sb_stemmer_list();
80
- while (*language != NULL) {
81
- rb_ary_push(out, rb_utf8_str_new_cstr(*language));
82
- language++;
83
- }
126
+ const char** language = sb_stemmer_list();
127
+ while (*language != NULL)
128
+ rb_ary_push(out, rb_utf8_str_new_cstr(*language++));
84
129
 
85
130
  return out;
86
131
  }
87
132
 
88
133
  void Init_ext(void)
89
134
  {
90
- VALUE rb_mMittens = rb_define_module("Mittens");
91
- VALUE rb_cStemmer = rb_define_class_under(rb_mMittens, "Stemmer", rb_cObject);
92
- rb_define_alloc_func(rb_cStemmer, stemmer_allocate);
93
- rb_define_method(rb_cStemmer, "initialize", stemmer_initialize, -1);
94
- rb_define_method(rb_cStemmer, "stem", stemmer_stem, 1);
95
- rb_define_singleton_method(rb_cStemmer, "languages", stemmer_languages, 0);
135
+ VALUE mMittens = rb_define_module("Mittens");
136
+ VALUE cStemmer = rb_define_class_under(mMittens, "Stemmer", rb_cObject);
137
+ rb_define_alloc_func(cStemmer, stemmer_allocate);
138
+ rb_define_method(cStemmer, "initialize", stemmer_initialize, -1);
139
+ rb_define_method(cStemmer, "stem", stemmer_stem, 1);
140
+ rb_define_method(cStemmer, "initialize_copy", stemmer_initialize_copy, 1);
141
+ rb_define_singleton_method(cStemmer, "languages", stemmer_languages, 0);
96
142
  }
@@ -10,6 +10,8 @@ output, status = Open3.capture2(make_cmd, "CFLAGS=#{cflags}", chdir: vendor)
10
10
  puts output
11
11
  raise "Command failed" unless status.success?
12
12
 
13
+ append_cflags("-Wall -Wextra -Wpedantic -Wconversion")
14
+
13
15
  $INCFLAGS += " -I$(srcdir)/../../vendor/snowball/include"
14
16
  $LDFLAGS += " $(srcdir)/../../vendor/snowball/libstemmer.a"
15
17
 
@@ -1,3 +1,3 @@
1
1
  module Mittens
2
- VERSION = "0.3.2"
2
+ VERSION = "0.3.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mittens
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane