mittens 0.3.3 → 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: a293e52f3e6ecf7669615bbf24aae34a9fcef78305b7324f7f739fc97e7eb0fc
4
- data.tar.gz: e5f1ba1b94ac634316599a25d969c1d71d4a0ff591ef043011c2481fe641814d
3
+ metadata.gz: d7e35e680454076f43e5973db399645697e0e98732555293d6b5e3bae115308d
4
+ data.tar.gz: 6c619fe9518ba5ddd42001ee0e99b4883b0fa703d5bf7b45e9022a039c78fb7d
5
5
  SHA512:
6
- metadata.gz: 4c392905c194b9eb723f44e7e97fd040395757751a43a034c34dfef51f399f24e2be4e390414e6e34e79bd26410987d696cd23fa197396ff9510f60a5503730e
7
- data.tar.gz: 9dbff4611a7d1b43d42d19fa0e873a3c5055b2358b5b0d75e3080e9686ccd315be564dbd93af62e28a63c3e1b50706968a2b6dbcd01f9dc7cd41420bf782ac09
6
+ metadata.gz: 1148ef35cf15d3027b51934844763437f1a606b2eed92dec9ccb17c86a78e544d664a51e6470da7d117c3a30fb1e0638119f3f1091fb203b29630e11513d5672
7
+ data.tar.gz: c1cc877ce40bf0c2fc3dd9a7946b4cf0165f870392ff87024c3a717cfe742420b8de6b1bbe8ecf5645e0566f81ed906f1ce87a9072ba645c4ca3096702b253d8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.3.4 (2026-08-06)
2
+
3
+ - Fixed `dup` and `clone` methods
4
+
1
5
  ## 0.3.3 (2026-08-05)
2
6
 
3
7
  - Fixed issue with GC compaction
data/ext/mittens/ext.c CHANGED
@@ -3,21 +3,35 @@
3
3
  #include "libstemmer.h"
4
4
  #include "ruby/ruby.h"
5
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
+
6
9
  typedef struct stemmer {
7
10
  struct sb_stemmer* stemmer;
11
+ VALUE language;
8
12
  } stemmer_t;
9
13
 
14
+ static void stemmer_mark(void* ptr)
15
+ {
16
+ stemmer_t* stemmer = (stemmer_t*) ptr;
17
+ rb_gc_mark(stemmer->language);
18
+ }
19
+
10
20
  static void stemmer_free(void* ptr)
11
21
  {
12
22
  stemmer_t* stemmer = (stemmer_t*) ptr;
13
- // safe to pass null pointer according to docs
14
- sb_stemmer_delete(stemmer->stemmer);
23
+
24
+ // safe to pass null pointer, but check anyways
25
+ if (stemmer->stemmer != NULL)
26
+ sb_stemmer_delete(stemmer->stemmer);
27
+
15
28
  xfree(ptr);
16
29
  }
17
30
 
18
31
  const rb_data_type_t stemmer_data_type = {
19
- .wrap_struct_name = "stemmer",
32
+ .wrap_struct_name = "Mittens::Stemmer",
20
33
  .function = {
34
+ .dmark = stemmer_mark,
21
35
  .dfree = stemmer_free,
22
36
  },
23
37
  .flags = RUBY_TYPED_FREE_IMMEDIATELY
@@ -28,38 +42,37 @@ static VALUE stemmer_allocate(VALUE klass)
28
42
  stemmer_t* stemmer;
29
43
  VALUE obj = TypedData_Make_Struct(klass, stemmer_t, &stemmer_data_type, stemmer);
30
44
  stemmer->stemmer = NULL;
45
+ stemmer->language = Qnil;
31
46
  return obj;
32
47
  }
33
48
 
34
49
  static VALUE stemmer_initialize(int argc, VALUE* argv, VALUE self)
35
50
  {
51
+ stemmer_t* stemmer;
52
+ GetStemmer(self, stemmer);
53
+
54
+ if (stemmer->stemmer != NULL)
55
+ rb_raise(rb_eRuntimeError, "stemmer already initialized");
56
+
36
57
  VALUE opts;
37
58
  rb_scan_args(argc, argv, ":", &opts);
38
59
 
39
60
  VALUE language = Qnil;
40
- const char* algorithm = "english";
41
- if (!NIL_P(opts)) {
61
+ if (!NIL_P(opts))
42
62
  language = rb_hash_aref(opts, ID2SYM(rb_intern("language")));
43
- if (!NIL_P(language)) {
44
- Check_Type(language, T_STRING);
45
- algorithm = RSTRING_PTR(language);
46
- }
47
- }
48
-
49
- stemmer_t* stemmer;
50
- TypedData_Get_Struct(self, stemmer_t, &stemmer_data_type, stemmer);
51
-
52
- // in case called multiple times
53
- sb_stemmer_delete(stemmer->stemmer);
54
63
 
55
64
  // if adding support for encoding, may want to change encoding returned from stem
56
- stemmer->stemmer = sb_stemmer_new(algorithm, NULL);
57
- if (stemmer->stemmer == NULL) {
58
- 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");
59
72
  }
60
73
 
61
- // must be placed after last use of algorithm
62
- RB_GC_GUARD(language);
74
+ // copy (after GetAlgorithm) in case string is modified in future
75
+ stemmer->language = rb_obj_dup(language);
63
76
 
64
77
  return self;
65
78
  }
@@ -67,17 +80,43 @@ static VALUE stemmer_initialize(int argc, VALUE* argv, VALUE self)
67
80
  static VALUE stemmer_stem(VALUE self, VALUE value)
68
81
  {
69
82
  stemmer_t* stemmer;
70
- TypedData_Get_Struct(self, stemmer_t, &stemmer_data_type, stemmer);
83
+ GetStemmer(self, stemmer);
71
84
 
72
- Check_Type(value, T_STRING);
85
+ if (stemmer->stemmer == NULL)
86
+ rb_raise(rb_eRuntimeError, "stemmer not initialized");
73
87
 
74
- const sb_symbol* word = (const sb_symbol*) RSTRING_PTR(value);
88
+ const char* word = StringValuePtr(value);
75
89
  long size = RSTRING_LEN(value);
76
90
  if (size > INT_MAX)
77
91
  rb_raise(rb_eArgError, "string exceeds max length");
78
- const sb_symbol* pointer_out = sb_stemmer_stem(stemmer->stemmer, word, (int) size);
79
92
 
80
- return rb_utf8_str_new_cstr((char*) pointer_out);
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);
95
+
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;
81
120
  }
82
121
 
83
122
  static VALUE stemmer_languages(VALUE klass)
@@ -85,10 +124,8 @@ static VALUE stemmer_languages(VALUE klass)
85
124
  VALUE out = rb_ary_new();
86
125
 
87
126
  const char** language = sb_stemmer_list();
88
- while (*language != NULL) {
89
- rb_ary_push(out, rb_utf8_str_new_cstr(*language));
90
- language++;
91
- }
127
+ while (*language != NULL)
128
+ rb_ary_push(out, rb_utf8_str_new_cstr(*language++));
92
129
 
93
130
  return out;
94
131
  }
@@ -100,5 +137,6 @@ void Init_ext(void)
100
137
  rb_define_alloc_func(cStemmer, stemmer_allocate);
101
138
  rb_define_method(cStemmer, "initialize", stemmer_initialize, -1);
102
139
  rb_define_method(cStemmer, "stem", stemmer_stem, 1);
140
+ rb_define_method(cStemmer, "initialize_copy", stemmer_initialize_copy, 1);
103
141
  rb_define_singleton_method(cStemmer, "languages", stemmer_languages, 0);
104
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.3"
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.3
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane