re2 2.26.2 → 2.28.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/Gemfile +5 -0
- data/README.md +1 -1
- data/ext/re2/extconf.rb +2 -26
- data/ext/re2/re2.cc +364 -155
- data/lib/re2/version.rb +1 -1
- data/re2.gemspec +1 -1
- data/spec/re2/match_data_spec.rb +90 -2
- data/spec/re2/regexp_spec.rb +85 -5
- data/spec/re2/scanner_spec.rb +9 -0
- data/spec/re2/set_spec.rb +70 -4
- data/spec/re2_spec.rb +60 -0
- metadata +4 -4
data/ext/re2/re2.cc
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
#include <re2/set.h>
|
|
20
20
|
#include <ruby.h>
|
|
21
21
|
#include <ruby/encoding.h>
|
|
22
|
+
#include <ruby/thread.h>
|
|
22
23
|
|
|
23
24
|
#define BOOL2RUBY(v) (v ? Qtrue : Qfalse)
|
|
24
25
|
|
|
@@ -41,8 +42,142 @@ typedef struct {
|
|
|
41
42
|
|
|
42
43
|
typedef struct {
|
|
43
44
|
RE2::Set *set;
|
|
45
|
+
bool compiled;
|
|
44
46
|
} re2_set;
|
|
45
47
|
|
|
48
|
+
struct nogvl_match_arg {
|
|
49
|
+
const RE2 *pattern;
|
|
50
|
+
re2::StringPiece text;
|
|
51
|
+
size_t startpos;
|
|
52
|
+
size_t endpos;
|
|
53
|
+
RE2::Anchor anchor;
|
|
54
|
+
re2::StringPiece *matches;
|
|
55
|
+
int n;
|
|
56
|
+
bool matched;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
static void *nogvl_match(void *ptr) {
|
|
60
|
+
auto *arg = static_cast<nogvl_match_arg *>(ptr);
|
|
61
|
+
#ifdef HAVE_ENDPOS_ARGUMENT
|
|
62
|
+
arg->matched = arg->pattern->Match(
|
|
63
|
+
arg->text, arg->startpos, arg->endpos,
|
|
64
|
+
arg->anchor, arg->matches, arg->n);
|
|
65
|
+
#else
|
|
66
|
+
arg->matched = arg->pattern->Match(
|
|
67
|
+
arg->text, arg->startpos,
|
|
68
|
+
arg->anchor, arg->matches, arg->n);
|
|
69
|
+
#endif
|
|
70
|
+
return nullptr;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static bool re2_match_without_gvl(
|
|
74
|
+
const RE2 *pattern, VALUE text, size_t startpos, size_t endpos,
|
|
75
|
+
RE2::Anchor anchor, re2::StringPiece *matches, int n) {
|
|
76
|
+
nogvl_match_arg arg;
|
|
77
|
+
arg.pattern = pattern;
|
|
78
|
+
arg.text = re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text));
|
|
79
|
+
arg.startpos = startpos;
|
|
80
|
+
arg.endpos = endpos;
|
|
81
|
+
arg.anchor = anchor;
|
|
82
|
+
arg.matches = matches;
|
|
83
|
+
arg.n = n;
|
|
84
|
+
arg.matched = false;
|
|
85
|
+
|
|
86
|
+
/* Abseil's synchronization primitives (SRWLOCK, SleepConditionVariableSRW)
|
|
87
|
+
* are incompatible with Ruby's Win32 Mutex-based GVL, causing
|
|
88
|
+
* WAIT_ABANDONED crashes when multiple threads match concurrently.
|
|
89
|
+
*/
|
|
90
|
+
#ifdef _WIN32
|
|
91
|
+
nogvl_match(&arg);
|
|
92
|
+
#else
|
|
93
|
+
/* No unblocking function is needed: RE2 matching is CPU-bound computation,
|
|
94
|
+
* not a blocking system call, so a signal cannot safely interrupt it.
|
|
95
|
+
*/
|
|
96
|
+
rb_thread_call_without_gvl(nogvl_match, &arg, NULL, NULL);
|
|
97
|
+
#endif
|
|
98
|
+
|
|
99
|
+
return arg.matched;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
struct nogvl_set_match_arg {
|
|
103
|
+
const RE2::Set *set;
|
|
104
|
+
re2::StringPiece text;
|
|
105
|
+
std::vector<int> *v;
|
|
106
|
+
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
107
|
+
RE2::Set::ErrorInfo *error_info;
|
|
108
|
+
#endif
|
|
109
|
+
bool matched;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
static void *nogvl_set_match(void *ptr) {
|
|
113
|
+
auto *arg = static_cast<nogvl_set_match_arg *>(ptr);
|
|
114
|
+
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
115
|
+
if (arg->error_info) {
|
|
116
|
+
arg->matched = arg->set->Match(arg->text, arg->v, arg->error_info);
|
|
117
|
+
} else {
|
|
118
|
+
arg->matched = arg->set->Match(arg->text, arg->v);
|
|
119
|
+
}
|
|
120
|
+
#else
|
|
121
|
+
arg->matched = arg->set->Match(arg->text, arg->v);
|
|
122
|
+
#endif
|
|
123
|
+
return nullptr;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
struct nogvl_replace_arg {
|
|
127
|
+
std::string *str;
|
|
128
|
+
const RE2 *pattern;
|
|
129
|
+
re2::StringPiece string_pattern;
|
|
130
|
+
re2::StringPiece rewrite;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
static void *nogvl_replace(void *ptr) {
|
|
134
|
+
auto *arg = static_cast<nogvl_replace_arg *>(ptr);
|
|
135
|
+
if (arg->pattern) {
|
|
136
|
+
RE2::Replace(arg->str, *arg->pattern, arg->rewrite);
|
|
137
|
+
} else {
|
|
138
|
+
RE2::Replace(arg->str, arg->string_pattern, arg->rewrite);
|
|
139
|
+
}
|
|
140
|
+
return nullptr;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static void *nogvl_global_replace(void *ptr) {
|
|
144
|
+
auto *arg = static_cast<nogvl_replace_arg *>(ptr);
|
|
145
|
+
if (arg->pattern) {
|
|
146
|
+
RE2::GlobalReplace(arg->str, *arg->pattern, arg->rewrite);
|
|
147
|
+
} else {
|
|
148
|
+
RE2::GlobalReplace(arg->str, arg->string_pattern, arg->rewrite);
|
|
149
|
+
}
|
|
150
|
+
return nullptr;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
struct nogvl_extract_arg {
|
|
154
|
+
re2::StringPiece text;
|
|
155
|
+
const RE2 *pattern;
|
|
156
|
+
re2::StringPiece string_pattern;
|
|
157
|
+
re2::StringPiece rewrite;
|
|
158
|
+
std::string *out;
|
|
159
|
+
bool extracted;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
static void *nogvl_extract(void *ptr) {
|
|
163
|
+
auto *arg = static_cast<nogvl_extract_arg *>(ptr);
|
|
164
|
+
if (arg->pattern) {
|
|
165
|
+
arg->extracted = RE2::Extract(arg->text, *arg->pattern,
|
|
166
|
+
arg->rewrite, arg->out);
|
|
167
|
+
} else {
|
|
168
|
+
arg->extracted = RE2::Extract(arg->text, RE2(arg->string_pattern),
|
|
169
|
+
arg->rewrite, arg->out);
|
|
170
|
+
}
|
|
171
|
+
return nullptr;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
static std::string group_name(VALUE key) {
|
|
175
|
+
VALUE str = SYMBOL_P(key) ? rb_sym2str(key) : key;
|
|
176
|
+
str = rb_str_conv_enc(str, rb_enc_get(str), rb_utf8_encoding());
|
|
177
|
+
|
|
178
|
+
return std::string(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
179
|
+
}
|
|
180
|
+
|
|
46
181
|
VALUE re2_mRE2, re2_cRegexp, re2_cMatchData, re2_cScanner, re2_cSet,
|
|
47
182
|
re2_eSetMatchError, re2_eSetUnsupportedError, re2_eRegexpUnsupportedError;
|
|
48
183
|
|
|
@@ -126,7 +261,7 @@ static void parse_re2_options(RE2::Options* re2_options, const VALUE options) {
|
|
|
126
261
|
}
|
|
127
262
|
|
|
128
263
|
static void re2_matchdata_mark(void *ptr) {
|
|
129
|
-
re2_matchdata *m =
|
|
264
|
+
re2_matchdata *m = static_cast<re2_matchdata *>(ptr);
|
|
130
265
|
rb_gc_mark_movable(m->regexp);
|
|
131
266
|
|
|
132
267
|
/* Text must not be movable because StringPiece matches hold pointers into
|
|
@@ -136,12 +271,12 @@ static void re2_matchdata_mark(void *ptr) {
|
|
|
136
271
|
}
|
|
137
272
|
|
|
138
273
|
static void re2_matchdata_compact(void *ptr) {
|
|
139
|
-
re2_matchdata *m =
|
|
274
|
+
re2_matchdata *m = static_cast<re2_matchdata *>(ptr);
|
|
140
275
|
m->regexp = rb_gc_location(m->regexp);
|
|
141
276
|
}
|
|
142
277
|
|
|
143
278
|
static void re2_matchdata_free(void *ptr) {
|
|
144
|
-
re2_matchdata *m =
|
|
279
|
+
re2_matchdata *m = static_cast<re2_matchdata *>(ptr);
|
|
145
280
|
if (m->matches) {
|
|
146
281
|
delete[] m->matches;
|
|
147
282
|
}
|
|
@@ -149,7 +284,7 @@ static void re2_matchdata_free(void *ptr) {
|
|
|
149
284
|
}
|
|
150
285
|
|
|
151
286
|
static size_t re2_matchdata_memsize(const void *ptr) {
|
|
152
|
-
const re2_matchdata *m =
|
|
287
|
+
const re2_matchdata *m = static_cast<const re2_matchdata *>(ptr);
|
|
153
288
|
size_t size = sizeof(*m);
|
|
154
289
|
if (m->matches) {
|
|
155
290
|
size += sizeof(*m->matches) * m->number_of_matches;
|
|
@@ -174,7 +309,7 @@ static const rb_data_type_t re2_matchdata_data_type = {
|
|
|
174
309
|
};
|
|
175
310
|
|
|
176
311
|
static void re2_scanner_mark(void *ptr) {
|
|
177
|
-
re2_scanner *s =
|
|
312
|
+
re2_scanner *s = static_cast<re2_scanner *>(ptr);
|
|
178
313
|
rb_gc_mark_movable(s->regexp);
|
|
179
314
|
|
|
180
315
|
/* Text must not be movable because the StringPiece input holds a pointer
|
|
@@ -184,12 +319,12 @@ static void re2_scanner_mark(void *ptr) {
|
|
|
184
319
|
}
|
|
185
320
|
|
|
186
321
|
static void re2_scanner_compact(void *ptr) {
|
|
187
|
-
re2_scanner *s =
|
|
322
|
+
re2_scanner *s = static_cast<re2_scanner *>(ptr);
|
|
188
323
|
s->regexp = rb_gc_location(s->regexp);
|
|
189
324
|
}
|
|
190
325
|
|
|
191
326
|
static void re2_scanner_free(void *ptr) {
|
|
192
|
-
re2_scanner *s =
|
|
327
|
+
re2_scanner *s = static_cast<re2_scanner *>(ptr);
|
|
193
328
|
if (s->input) {
|
|
194
329
|
delete s->input;
|
|
195
330
|
}
|
|
@@ -197,7 +332,7 @@ static void re2_scanner_free(void *ptr) {
|
|
|
197
332
|
}
|
|
198
333
|
|
|
199
334
|
static size_t re2_scanner_memsize(const void *ptr) {
|
|
200
|
-
const re2_scanner *s =
|
|
335
|
+
const re2_scanner *s = static_cast<const re2_scanner *>(ptr);
|
|
201
336
|
size_t size = sizeof(*s);
|
|
202
337
|
if (s->input) {
|
|
203
338
|
size += sizeof(*s->input);
|
|
@@ -222,7 +357,7 @@ static const rb_data_type_t re2_scanner_data_type = {
|
|
|
222
357
|
};
|
|
223
358
|
|
|
224
359
|
static void re2_regexp_free(void *ptr) {
|
|
225
|
-
re2_pattern *p =
|
|
360
|
+
re2_pattern *p = static_cast<re2_pattern *>(ptr);
|
|
226
361
|
if (p->pattern) {
|
|
227
362
|
delete p->pattern;
|
|
228
363
|
}
|
|
@@ -230,7 +365,7 @@ static void re2_regexp_free(void *ptr) {
|
|
|
230
365
|
}
|
|
231
366
|
|
|
232
367
|
static size_t re2_regexp_memsize(const void *ptr) {
|
|
233
|
-
const re2_pattern *p =
|
|
368
|
+
const re2_pattern *p = static_cast<const re2_pattern *>(ptr);
|
|
234
369
|
size_t size = sizeof(*p);
|
|
235
370
|
if (p->pattern) {
|
|
236
371
|
size += sizeof(*p->pattern);
|
|
@@ -250,7 +385,7 @@ static const rb_data_type_t re2_regexp_data_type = {
|
|
|
250
385
|
0,
|
|
251
386
|
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
|
|
252
387
|
// macro to update VALUE references, as to trigger write barriers.
|
|
253
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
|
388
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE
|
|
254
389
|
};
|
|
255
390
|
|
|
256
391
|
static re2_pattern *unwrap_re2_regexp(VALUE self) {
|
|
@@ -285,9 +420,9 @@ static re2_scanner *unwrap_re2_scanner(VALUE self) {
|
|
|
285
420
|
* in alphabetical order rather than definition order, as RE2 stores named
|
|
286
421
|
* groups internally in a sorted map.
|
|
287
422
|
*
|
|
288
|
-
* Note RE2
|
|
289
|
-
*
|
|
290
|
-
*
|
|
423
|
+
* Note RE2 stores capturing group names as UTF-8, even if the {RE2::Regexp}
|
|
424
|
+
* sets the `:utf8` option to false so strings will always be returned in
|
|
425
|
+
* UTF-8.
|
|
291
426
|
*
|
|
292
427
|
* @return [Array<String>] an array of names of named capturing groups
|
|
293
428
|
* @example
|
|
@@ -300,9 +435,7 @@ static VALUE re2_regexp_names(const VALUE self) {
|
|
|
300
435
|
VALUE names = rb_ary_new2(groups.size());
|
|
301
436
|
|
|
302
437
|
for (const auto& group : groups) {
|
|
303
|
-
rb_ary_push(names,
|
|
304
|
-
encoded_str_new(group.first.data(), group.first.size(),
|
|
305
|
-
p->pattern->options().encoding()));
|
|
438
|
+
rb_ary_push(names, rb_utf8_str_new(group.first.data(), group.first.size()));
|
|
306
439
|
}
|
|
307
440
|
|
|
308
441
|
return names;
|
|
@@ -396,6 +529,8 @@ static VALUE re2_scanner_rewind(VALUE self) {
|
|
|
396
529
|
}
|
|
397
530
|
|
|
398
531
|
static VALUE re2_scanner_initialize_copy(VALUE self, VALUE other) {
|
|
532
|
+
if (self == other) return self;
|
|
533
|
+
|
|
399
534
|
re2_scanner *self_c;
|
|
400
535
|
re2_scanner *other_c = unwrap_re2_scanner(other);
|
|
401
536
|
|
|
@@ -519,20 +654,13 @@ static re2::StringPiece *re2_matchdata_find_match(VALUE idx, const VALUE self) {
|
|
|
519
654
|
|
|
520
655
|
if (RB_INTEGER_TYPE_P(idx)) {
|
|
521
656
|
id = NUM2INT(idx);
|
|
522
|
-
} else if (SYMBOL_P(idx)) {
|
|
523
|
-
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
524
|
-
auto search = groups.find(rb_id2name(SYM2ID(idx)));
|
|
525
|
-
|
|
526
|
-
if (search != groups.end()) {
|
|
527
|
-
id = search->second;
|
|
528
|
-
} else {
|
|
529
|
-
return nullptr;
|
|
530
|
-
}
|
|
531
657
|
} else {
|
|
532
|
-
|
|
658
|
+
if (!SYMBOL_P(idx)) {
|
|
659
|
+
StringValue(idx);
|
|
660
|
+
}
|
|
533
661
|
|
|
534
662
|
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
535
|
-
auto search = groups.find(
|
|
663
|
+
auto search = groups.find(group_name(idx));
|
|
536
664
|
|
|
537
665
|
if (search != groups.end()) {
|
|
538
666
|
id = search->second;
|
|
@@ -877,11 +1005,8 @@ static VALUE re2_matchdata_aref(int argc, VALUE *argv, const VALUE self) {
|
|
|
877
1005
|
VALUE idx, rest;
|
|
878
1006
|
rb_scan_args(argc, argv, "11", &idx, &rest);
|
|
879
1007
|
|
|
880
|
-
if (TYPE(idx) == T_STRING) {
|
|
881
|
-
return re2_matchdata_named_match(
|
|
882
|
-
std::string(RSTRING_PTR(idx), RSTRING_LEN(idx)), self);
|
|
883
|
-
} else if (SYMBOL_P(idx)) {
|
|
884
|
-
return re2_matchdata_named_match(rb_id2name(SYM2ID(idx)), self);
|
|
1008
|
+
if (TYPE(idx) == T_STRING || SYMBOL_P(idx)) {
|
|
1009
|
+
return re2_matchdata_named_match(group_name(idx), self);
|
|
885
1010
|
} else if (!NIL_P(rest) || !RB_INTEGER_TYPE_P(idx) || NUM2INT(idx) < 0) {
|
|
886
1011
|
return rb_ary_aref(argc, argv, re2_matchdata_to_a(self));
|
|
887
1012
|
} else {
|
|
@@ -995,6 +1120,7 @@ static VALUE re2_matchdata_deconstruct(const VALUE self) {
|
|
|
995
1120
|
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
996
1121
|
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
997
1122
|
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
1123
|
+
* Capturing group names will always be returned in UTF-8.
|
|
998
1124
|
*
|
|
999
1125
|
* @return [Hash] a hash of capturing group names to submatches
|
|
1000
1126
|
* @param [Array<Symbol>, nil] keys an array of `Symbol` capturing group names
|
|
@@ -1024,7 +1150,7 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
|
|
|
1024
1150
|
if (NIL_P(keys)) {
|
|
1025
1151
|
for (const auto& group : groups) {
|
|
1026
1152
|
rb_hash_aset(capturing_groups,
|
|
1027
|
-
|
|
1153
|
+
rb_str_intern(rb_utf8_str_new(group.first.data(), group.first.size())),
|
|
1028
1154
|
re2_matchdata_nth_match(group.second, self));
|
|
1029
1155
|
}
|
|
1030
1156
|
} else {
|
|
@@ -1034,8 +1160,7 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
|
|
|
1034
1160
|
for (int i = 0; i < RARRAY_LEN(keys); ++i) {
|
|
1035
1161
|
VALUE key = rb_ary_entry(keys, i);
|
|
1036
1162
|
Check_Type(key, T_SYMBOL);
|
|
1037
|
-
|
|
1038
|
-
auto search = groups.find(name);
|
|
1163
|
+
auto search = groups.find(group_name(key));
|
|
1039
1164
|
|
|
1040
1165
|
if (search != groups.end()) {
|
|
1041
1166
|
rb_hash_aset(capturing_groups, key, re2_matchdata_nth_match(search->second, self));
|
|
@@ -1055,6 +1180,7 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
|
|
|
1055
1180
|
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
1056
1181
|
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
1057
1182
|
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
1183
|
+
* Capturing group names will always be returned in UTF-8.
|
|
1058
1184
|
*
|
|
1059
1185
|
* @overload named_captures
|
|
1060
1186
|
* Returns a hash with string keys.
|
|
@@ -1092,12 +1218,9 @@ static VALUE re2_matchdata_named_captures(int argc, VALUE *argv, const VALUE sel
|
|
|
1092
1218
|
VALUE result = rb_hash_new();
|
|
1093
1219
|
|
|
1094
1220
|
for (const auto& group : groups) {
|
|
1095
|
-
VALUE key;
|
|
1221
|
+
VALUE key = rb_utf8_str_new(group.first.data(), group.first.size());
|
|
1096
1222
|
if (symbolize) {
|
|
1097
|
-
key =
|
|
1098
|
-
} else {
|
|
1099
|
-
key = encoded_str_new(group.first.data(), group.first.size(),
|
|
1100
|
-
p->pattern->options().encoding());
|
|
1223
|
+
key = rb_str_intern(key);
|
|
1101
1224
|
}
|
|
1102
1225
|
rb_hash_aset(result, key, re2_matchdata_nth_match(group.second, self));
|
|
1103
1226
|
}
|
|
@@ -1110,9 +1233,9 @@ static VALUE re2_matchdata_named_captures(int argc, VALUE *argv, const VALUE sel
|
|
|
1110
1233
|
* alphabetical order rather than definition order, as RE2 stores named groups
|
|
1111
1234
|
* internally in a sorted map.
|
|
1112
1235
|
*
|
|
1113
|
-
* Note RE2
|
|
1114
|
-
*
|
|
1115
|
-
*
|
|
1236
|
+
* Note RE2 stores capturing group names as UTF-8, even if the {RE2::Regexp}
|
|
1237
|
+
* sets the `:utf8` option to false so strings will always be returned in
|
|
1238
|
+
* UTF-8.
|
|
1116
1239
|
*
|
|
1117
1240
|
* @return [Array<String>] an array of names of named capturing groups
|
|
1118
1241
|
* @example
|
|
@@ -1149,12 +1272,8 @@ static VALUE re2_matchdata_values_at(int argc, VALUE *argv, const VALUE self) {
|
|
|
1149
1272
|
for (int i = 0; i < argc; ++i) {
|
|
1150
1273
|
VALUE idx = argv[i];
|
|
1151
1274
|
|
|
1152
|
-
if (TYPE(idx) == T_STRING) {
|
|
1153
|
-
rb_ary_push(result, re2_matchdata_named_match(
|
|
1154
|
-
std::string(RSTRING_PTR(idx), RSTRING_LEN(idx)), self));
|
|
1155
|
-
} else if (SYMBOL_P(idx)) {
|
|
1156
|
-
rb_ary_push(result, re2_matchdata_named_match(
|
|
1157
|
-
rb_id2name(SYM2ID(idx)), self));
|
|
1275
|
+
if (TYPE(idx) == T_STRING || SYMBOL_P(idx)) {
|
|
1276
|
+
rb_ary_push(result, re2_matchdata_named_match(group_name(idx), self));
|
|
1158
1277
|
} else {
|
|
1159
1278
|
rb_ary_push(result, re2_matchdata_nth_match(NUM2INT(idx), self));
|
|
1160
1279
|
}
|
|
@@ -1164,6 +1283,8 @@ static VALUE re2_matchdata_values_at(int argc, VALUE *argv, const VALUE self) {
|
|
|
1164
1283
|
}
|
|
1165
1284
|
|
|
1166
1285
|
static VALUE re2_matchdata_initialize_copy(VALUE self, VALUE other) {
|
|
1286
|
+
if (self == other) return self;
|
|
1287
|
+
|
|
1167
1288
|
re2_matchdata *self_m;
|
|
1168
1289
|
re2_matchdata *other_m = unwrap_re2_matchdata(other);
|
|
1169
1290
|
|
|
@@ -1249,26 +1370,23 @@ static VALUE re2_regexp_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
1249
1370
|
|
|
1250
1371
|
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1251
1372
|
|
|
1252
|
-
|
|
1253
|
-
delete p->pattern;
|
|
1254
|
-
p->pattern = nullptr;
|
|
1255
|
-
}
|
|
1373
|
+
RE2::Options re2_options;
|
|
1256
1374
|
|
|
1257
1375
|
if (RTEST(options)) {
|
|
1258
|
-
RE2::Options re2_options;
|
|
1259
1376
|
parse_re2_options(&re2_options, options);
|
|
1260
|
-
|
|
1261
|
-
p->pattern = new(std::nothrow) RE2(
|
|
1262
|
-
re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)), re2_options);
|
|
1263
|
-
} else {
|
|
1264
|
-
p->pattern = new(std::nothrow) RE2(
|
|
1265
|
-
re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)));
|
|
1266
1377
|
}
|
|
1267
1378
|
|
|
1379
|
+
rb_check_frozen(self);
|
|
1380
|
+
|
|
1381
|
+
p->pattern = new(std::nothrow) RE2(
|
|
1382
|
+
re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)), re2_options);
|
|
1383
|
+
|
|
1268
1384
|
if (p->pattern == nullptr) {
|
|
1269
1385
|
rb_raise(rb_eNoMemError, "not enough memory to allocate RE2 object");
|
|
1270
1386
|
}
|
|
1271
1387
|
|
|
1388
|
+
rb_obj_freeze(self);
|
|
1389
|
+
|
|
1272
1390
|
return self;
|
|
1273
1391
|
}
|
|
1274
1392
|
|
|
@@ -1278,6 +1396,8 @@ static VALUE re2_regexp_initialize_copy(VALUE self, VALUE other) {
|
|
|
1278
1396
|
|
|
1279
1397
|
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, self_p);
|
|
1280
1398
|
|
|
1399
|
+
rb_check_frozen(self);
|
|
1400
|
+
|
|
1281
1401
|
if (self_p->pattern) {
|
|
1282
1402
|
delete self_p->pattern;
|
|
1283
1403
|
self_p->pattern = nullptr;
|
|
@@ -1289,6 +1409,8 @@ static VALUE re2_regexp_initialize_copy(VALUE self, VALUE other) {
|
|
|
1289
1409
|
rb_raise(rb_eNoMemError, "not enough memory to allocate RE2 object");
|
|
1290
1410
|
}
|
|
1291
1411
|
|
|
1412
|
+
rb_obj_freeze(self);
|
|
1413
|
+
|
|
1292
1414
|
return self;
|
|
1293
1415
|
}
|
|
1294
1416
|
|
|
@@ -1643,9 +1765,9 @@ static VALUE re2_regexp_number_of_capturing_groups(const VALUE self) {
|
|
|
1643
1765
|
/*
|
|
1644
1766
|
* Returns a hash of names to capturing indices of groups.
|
|
1645
1767
|
*
|
|
1646
|
-
* Note RE2
|
|
1647
|
-
*
|
|
1648
|
-
*
|
|
1768
|
+
* Note RE2 stores capturing group names as UTF-8, even if the {RE2::Regexp}
|
|
1769
|
+
* sets the `:utf8` option to false so strings will always be returned in
|
|
1770
|
+
* UTF-8.
|
|
1649
1771
|
*
|
|
1650
1772
|
* @return [Hash] a hash of names to capturing indices
|
|
1651
1773
|
*/
|
|
@@ -1656,8 +1778,7 @@ static VALUE re2_regexp_named_capturing_groups(const VALUE self) {
|
|
|
1656
1778
|
|
|
1657
1779
|
for (const auto& group : groups) {
|
|
1658
1780
|
rb_hash_aset(capturing_groups,
|
|
1659
|
-
|
|
1660
|
-
p->pattern->options().encoding()),
|
|
1781
|
+
rb_utf8_str_new(group.first.data(), group.first.size()),
|
|
1661
1782
|
INT2FIX(group.second));
|
|
1662
1783
|
}
|
|
1663
1784
|
|
|
@@ -1753,8 +1874,9 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1753
1874
|
|
|
1754
1875
|
rb_scan_args(argc, argv, "11", &text, &options);
|
|
1755
1876
|
|
|
1756
|
-
/*
|
|
1877
|
+
/* Coerce and freeze text to prevent mutation. */
|
|
1757
1878
|
StringValue(text);
|
|
1879
|
+
text = rb_str_new_frozen(text);
|
|
1758
1880
|
|
|
1759
1881
|
p = unwrap_re2_regexp(self);
|
|
1760
1882
|
|
|
@@ -1852,15 +1974,10 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1852
1974
|
#endif
|
|
1853
1975
|
|
|
1854
1976
|
if (n == 0) {
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
#else
|
|
1860
|
-
bool matched = p->pattern->Match(
|
|
1861
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)),
|
|
1862
|
-
startpos, anchor, 0, 0);
|
|
1863
|
-
#endif
|
|
1977
|
+
bool matched = re2_match_without_gvl(
|
|
1978
|
+
p->pattern, text, startpos, endpos, anchor, 0, 0);
|
|
1979
|
+
RB_GC_GUARD(text);
|
|
1980
|
+
|
|
1864
1981
|
return BOOL2RUBY(matched);
|
|
1865
1982
|
} else {
|
|
1866
1983
|
if (n == INT_MAX) {
|
|
@@ -1876,17 +1993,10 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1876
1993
|
"not enough memory to allocate StringPieces for matches");
|
|
1877
1994
|
}
|
|
1878
1995
|
|
|
1879
|
-
|
|
1996
|
+
bool matched = re2_match_without_gvl(
|
|
1997
|
+
p->pattern, text, startpos, endpos, anchor, matches, n);
|
|
1998
|
+
RB_GC_GUARD(text);
|
|
1880
1999
|
|
|
1881
|
-
#ifdef HAVE_ENDPOS_ARGUMENT
|
|
1882
|
-
bool matched = p->pattern->Match(
|
|
1883
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)),
|
|
1884
|
-
startpos, endpos, anchor, matches, n);
|
|
1885
|
-
#else
|
|
1886
|
-
bool matched = p->pattern->Match(
|
|
1887
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)),
|
|
1888
|
-
startpos, anchor, matches, n);
|
|
1889
|
-
#endif
|
|
1890
2000
|
if (matched) {
|
|
1891
2001
|
VALUE matchdata = rb_class_new_instance(0, 0, re2_cMatchData);
|
|
1892
2002
|
TypedData_Get_Struct(matchdata, re2_matchdata, &re2_matchdata_data_type, m);
|
|
@@ -1915,13 +2025,15 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1915
2025
|
* @raise [TypeError] if text cannot be coerced to a `String`
|
|
1916
2026
|
*/
|
|
1917
2027
|
static VALUE re2_regexp_match_p(const VALUE self, VALUE text) {
|
|
1918
|
-
/* Ensure text is a string. */
|
|
1919
2028
|
StringValue(text);
|
|
2029
|
+
text = rb_str_new_frozen(text);
|
|
1920
2030
|
|
|
1921
2031
|
re2_pattern *p = unwrap_re2_regexp(self);
|
|
2032
|
+
bool matched = re2_match_without_gvl(
|
|
2033
|
+
p->pattern, text, 0, RSTRING_LEN(text), RE2::UNANCHORED, 0, 0);
|
|
2034
|
+
RB_GC_GUARD(text);
|
|
1922
2035
|
|
|
1923
|
-
return BOOL2RUBY(
|
|
1924
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)), *p->pattern));
|
|
2036
|
+
return BOOL2RUBY(matched);
|
|
1925
2037
|
}
|
|
1926
2038
|
|
|
1927
2039
|
/*
|
|
@@ -1934,13 +2046,15 @@ static VALUE re2_regexp_match_p(const VALUE self, VALUE text) {
|
|
|
1934
2046
|
* @raise [TypeError] if text cannot be coerced to a `String`
|
|
1935
2047
|
*/
|
|
1936
2048
|
static VALUE re2_regexp_full_match_p(const VALUE self, VALUE text) {
|
|
1937
|
-
/* Ensure text is a string. */
|
|
1938
2049
|
StringValue(text);
|
|
2050
|
+
text = rb_str_new_frozen(text);
|
|
1939
2051
|
|
|
1940
2052
|
re2_pattern *p = unwrap_re2_regexp(self);
|
|
2053
|
+
bool matched = re2_match_without_gvl(
|
|
2054
|
+
p->pattern, text, 0, RSTRING_LEN(text), RE2::ANCHOR_BOTH, 0, 0);
|
|
2055
|
+
RB_GC_GUARD(text);
|
|
1941
2056
|
|
|
1942
|
-
return BOOL2RUBY(
|
|
1943
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)), *p->pattern));
|
|
2057
|
+
return BOOL2RUBY(matched);
|
|
1944
2058
|
}
|
|
1945
2059
|
|
|
1946
2060
|
/*
|
|
@@ -1956,8 +2070,8 @@ static VALUE re2_regexp_full_match_p(const VALUE self, VALUE text) {
|
|
|
1956
2070
|
* #=> #<RE2::Scanner:0x0000000000000001>
|
|
1957
2071
|
*/
|
|
1958
2072
|
static VALUE re2_regexp_scan(const VALUE self, VALUE text) {
|
|
1959
|
-
/* Ensure text is a string. */
|
|
1960
2073
|
StringValue(text);
|
|
2074
|
+
text = rb_str_new_frozen(text);
|
|
1961
2075
|
|
|
1962
2076
|
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1963
2077
|
re2_scanner *c;
|
|
@@ -1965,7 +2079,7 @@ static VALUE re2_regexp_scan(const VALUE self, VALUE text) {
|
|
|
1965
2079
|
TypedData_Get_Struct(scanner, re2_scanner, &re2_scanner_data_type, c);
|
|
1966
2080
|
|
|
1967
2081
|
RB_OBJ_WRITE(scanner, &c->regexp, self);
|
|
1968
|
-
RB_OBJ_WRITE(scanner, &c->text,
|
|
2082
|
+
RB_OBJ_WRITE(scanner, &c->text, text);
|
|
1969
2083
|
c->input = new(std::nothrow) re2::StringPiece(
|
|
1970
2084
|
RSTRING_PTR(c->text), RSTRING_LEN(c->text));
|
|
1971
2085
|
if (c->input == nullptr) {
|
|
@@ -2028,35 +2142,51 @@ static VALUE re2_replace(VALUE, VALUE str, VALUE pattern,
|
|
|
2028
2142
|
VALUE rewrite) {
|
|
2029
2143
|
re2_pattern *p = nullptr;
|
|
2030
2144
|
|
|
2031
|
-
/* Coerce all arguments before any C++ allocations so that any
|
|
2032
|
-
* exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2145
|
+
/* Coerce and freeze all arguments before any C++ allocations so that any
|
|
2146
|
+
* Ruby exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2147
|
+
* memory, and later coercions cannot mutate earlier strings.
|
|
2033
2148
|
*/
|
|
2034
2149
|
StringValue(str);
|
|
2150
|
+
str = rb_str_new_frozen(str);
|
|
2035
2151
|
if (rb_obj_is_kind_of(pattern, re2_cRegexp)) {
|
|
2036
2152
|
p = unwrap_re2_regexp(pattern);
|
|
2037
2153
|
} else {
|
|
2038
2154
|
StringValue(pattern);
|
|
2155
|
+
pattern = rb_str_new_frozen(pattern);
|
|
2039
2156
|
}
|
|
2040
2157
|
StringValue(rewrite);
|
|
2158
|
+
rewrite = rb_str_new_frozen(rewrite);
|
|
2041
2159
|
|
|
2042
|
-
/* Take a copy of str so it can be modified in-place by
|
|
2043
|
-
* RE2::Replace.
|
|
2044
|
-
*/
|
|
2160
|
+
/* Take a copy of str so it can be modified in-place by RE2::Replace. */
|
|
2045
2161
|
std::string str_as_string(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
2046
2162
|
|
|
2047
|
-
|
|
2163
|
+
nogvl_replace_arg arg;
|
|
2164
|
+
arg.str = &str_as_string;
|
|
2048
2165
|
if (p) {
|
|
2049
|
-
|
|
2050
|
-
|
|
2166
|
+
arg.pattern = p->pattern;
|
|
2167
|
+
} else {
|
|
2168
|
+
arg.pattern = nullptr;
|
|
2169
|
+
arg.string_pattern = re2::StringPiece(
|
|
2170
|
+
RSTRING_PTR(pattern), RSTRING_LEN(pattern));
|
|
2171
|
+
}
|
|
2172
|
+
arg.rewrite = re2::StringPiece(
|
|
2173
|
+
RSTRING_PTR(rewrite), RSTRING_LEN(rewrite));
|
|
2174
|
+
|
|
2175
|
+
#ifdef _WIN32
|
|
2176
|
+
nogvl_replace(&arg);
|
|
2177
|
+
#else
|
|
2178
|
+
rb_thread_call_without_gvl(nogvl_replace, &arg, NULL, NULL);
|
|
2179
|
+
#endif
|
|
2180
|
+
|
|
2181
|
+
RB_GC_GUARD(rewrite);
|
|
2182
|
+
RB_GC_GUARD(pattern);
|
|
2051
2183
|
|
|
2184
|
+
if (p) {
|
|
2052
2185
|
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
2053
2186
|
p->pattern->options().encoding());
|
|
2054
2187
|
} else {
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
re2::StringPiece(RSTRING_PTR(rewrite), RSTRING_LEN(rewrite)));
|
|
2058
|
-
|
|
2059
|
-
return encoded_str_new(str_as_string.data(), str_as_string.size(), RE2::Options::EncodingUTF8);
|
|
2188
|
+
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
2189
|
+
RE2::Options::EncodingUTF8);
|
|
2060
2190
|
}
|
|
2061
2191
|
}
|
|
2062
2192
|
|
|
@@ -2084,35 +2214,53 @@ static VALUE re2_global_replace(VALUE, VALUE str, VALUE pattern,
|
|
|
2084
2214
|
VALUE rewrite) {
|
|
2085
2215
|
re2_pattern *p = nullptr;
|
|
2086
2216
|
|
|
2087
|
-
/* Coerce all arguments before any C++ allocations so that any
|
|
2088
|
-
* exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2217
|
+
/* Coerce and freeze all arguments before any C++ allocations so that any
|
|
2218
|
+
* Ruby exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2219
|
+
* memory, and later coercions cannot mutate earlier strings.
|
|
2089
2220
|
*/
|
|
2090
2221
|
StringValue(str);
|
|
2222
|
+
str = rb_str_new_frozen(str);
|
|
2091
2223
|
if (rb_obj_is_kind_of(pattern, re2_cRegexp)) {
|
|
2092
2224
|
p = unwrap_re2_regexp(pattern);
|
|
2093
2225
|
} else {
|
|
2094
2226
|
StringValue(pattern);
|
|
2227
|
+
pattern = rb_str_new_frozen(pattern);
|
|
2095
2228
|
}
|
|
2096
2229
|
StringValue(rewrite);
|
|
2230
|
+
rewrite = rb_str_new_frozen(rewrite);
|
|
2097
2231
|
|
|
2098
2232
|
/* Take a copy of str so it can be modified in-place by
|
|
2099
2233
|
* RE2::GlobalReplace.
|
|
2100
2234
|
*/
|
|
2101
2235
|
std::string str_as_string(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
2102
2236
|
|
|
2103
|
-
|
|
2237
|
+
nogvl_replace_arg arg;
|
|
2238
|
+
arg.str = &str_as_string;
|
|
2104
2239
|
if (p) {
|
|
2105
|
-
|
|
2106
|
-
|
|
2240
|
+
arg.pattern = p->pattern;
|
|
2241
|
+
} else {
|
|
2242
|
+
arg.pattern = nullptr;
|
|
2243
|
+
arg.string_pattern = re2::StringPiece(
|
|
2244
|
+
RSTRING_PTR(pattern), RSTRING_LEN(pattern));
|
|
2245
|
+
}
|
|
2246
|
+
arg.rewrite = re2::StringPiece(
|
|
2247
|
+
RSTRING_PTR(rewrite), RSTRING_LEN(rewrite));
|
|
2107
2248
|
|
|
2249
|
+
#ifdef _WIN32
|
|
2250
|
+
nogvl_global_replace(&arg);
|
|
2251
|
+
#else
|
|
2252
|
+
rb_thread_call_without_gvl(nogvl_global_replace, &arg, NULL, NULL);
|
|
2253
|
+
#endif
|
|
2254
|
+
|
|
2255
|
+
RB_GC_GUARD(rewrite);
|
|
2256
|
+
RB_GC_GUARD(pattern);
|
|
2257
|
+
|
|
2258
|
+
if (p) {
|
|
2108
2259
|
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
2109
2260
|
p->pattern->options().encoding());
|
|
2110
2261
|
} else {
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
re2::StringPiece(RSTRING_PTR(rewrite), RSTRING_LEN(rewrite)));
|
|
2114
|
-
|
|
2115
|
-
return encoded_str_new(str_as_string.data(), str_as_string.size(), RE2::Options::EncodingUTF8);
|
|
2262
|
+
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
2263
|
+
RE2::Options::EncodingUTF8);
|
|
2116
2264
|
}
|
|
2117
2265
|
}
|
|
2118
2266
|
|
|
@@ -2142,46 +2290,53 @@ static VALUE re2_extract(VALUE, VALUE text, VALUE pattern,
|
|
|
2142
2290
|
VALUE rewrite) {
|
|
2143
2291
|
re2_pattern *p = nullptr;
|
|
2144
2292
|
|
|
2145
|
-
/* Coerce all arguments before any C++ allocations so that any
|
|
2146
|
-
* exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2293
|
+
/* Coerce and freeze all arguments before any C++ allocations so that any
|
|
2294
|
+
* Ruby exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2295
|
+
* memory, and later coercions cannot mutate earlier strings.
|
|
2147
2296
|
*/
|
|
2148
2297
|
StringValue(text);
|
|
2298
|
+
text = rb_str_new_frozen(text);
|
|
2149
2299
|
if (rb_obj_is_kind_of(pattern, re2_cRegexp)) {
|
|
2150
2300
|
p = unwrap_re2_regexp(pattern);
|
|
2151
2301
|
} else {
|
|
2152
2302
|
StringValue(pattern);
|
|
2303
|
+
pattern = rb_str_new_frozen(pattern);
|
|
2153
2304
|
}
|
|
2154
2305
|
StringValue(rewrite);
|
|
2306
|
+
rewrite = rb_str_new_frozen(rewrite);
|
|
2155
2307
|
|
|
2156
2308
|
std::string out;
|
|
2157
|
-
bool extracted;
|
|
2158
2309
|
|
|
2310
|
+
nogvl_extract_arg arg;
|
|
2311
|
+
arg.text = re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text));
|
|
2159
2312
|
if (p) {
|
|
2160
|
-
|
|
2161
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)),
|
|
2162
|
-
*p->pattern,
|
|
2163
|
-
re2::StringPiece(RSTRING_PTR(rewrite), RSTRING_LEN(rewrite)),
|
|
2164
|
-
&out);
|
|
2165
|
-
|
|
2166
|
-
if (extracted) {
|
|
2167
|
-
return encoded_str_new(out.data(), out.size(),
|
|
2168
|
-
p->pattern->options().encoding());
|
|
2169
|
-
} else {
|
|
2170
|
-
return Qnil;
|
|
2171
|
-
}
|
|
2313
|
+
arg.pattern = p->pattern;
|
|
2172
2314
|
} else {
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2315
|
+
arg.pattern = nullptr;
|
|
2316
|
+
arg.string_pattern = re2::StringPiece(
|
|
2317
|
+
RSTRING_PTR(pattern), RSTRING_LEN(pattern));
|
|
2318
|
+
}
|
|
2319
|
+
arg.rewrite = re2::StringPiece(
|
|
2320
|
+
RSTRING_PTR(rewrite), RSTRING_LEN(rewrite));
|
|
2321
|
+
arg.out = &out;
|
|
2322
|
+
arg.extracted = false;
|
|
2323
|
+
|
|
2324
|
+
#ifdef _WIN32
|
|
2325
|
+
nogvl_extract(&arg);
|
|
2326
|
+
#else
|
|
2327
|
+
rb_thread_call_without_gvl(nogvl_extract, &arg, NULL, NULL);
|
|
2328
|
+
#endif
|
|
2329
|
+
|
|
2330
|
+
RB_GC_GUARD(text);
|
|
2331
|
+
RB_GC_GUARD(rewrite);
|
|
2332
|
+
RB_GC_GUARD(pattern);
|
|
2333
|
+
|
|
2334
|
+
if (arg.extracted) {
|
|
2335
|
+
return encoded_str_new(out.data(), out.size(),
|
|
2336
|
+
p ? p->pattern->options().encoding()
|
|
2337
|
+
: RE2::Options::EncodingUTF8);
|
|
2338
|
+
} else {
|
|
2339
|
+
return Qnil;
|
|
2185
2340
|
}
|
|
2186
2341
|
}
|
|
2187
2342
|
|
|
@@ -2211,7 +2366,7 @@ static VALUE re2_escape(VALUE, VALUE unquoted) {
|
|
|
2211
2366
|
}
|
|
2212
2367
|
|
|
2213
2368
|
static void re2_set_free(void *ptr) {
|
|
2214
|
-
re2_set *s =
|
|
2369
|
+
re2_set *s = static_cast<re2_set *>(ptr);
|
|
2215
2370
|
if (s->set) {
|
|
2216
2371
|
delete s->set;
|
|
2217
2372
|
}
|
|
@@ -2219,7 +2374,7 @@ static void re2_set_free(void *ptr) {
|
|
|
2219
2374
|
}
|
|
2220
2375
|
|
|
2221
2376
|
static size_t re2_set_memsize(const void *ptr) {
|
|
2222
|
-
const re2_set *s =
|
|
2377
|
+
const re2_set *s = static_cast<const re2_set *>(ptr);
|
|
2223
2378
|
size_t size = sizeof(*s);
|
|
2224
2379
|
if (s->set) {
|
|
2225
2380
|
size += sizeof(*s->set);
|
|
@@ -2239,7 +2394,7 @@ static const rb_data_type_t re2_set_data_type = {
|
|
|
2239
2394
|
0,
|
|
2240
2395
|
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
|
|
2241
2396
|
// macro to update VALUE references, as to trigger write barriers.
|
|
2242
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
|
2397
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE
|
|
2243
2398
|
};
|
|
2244
2399
|
|
|
2245
2400
|
static re2_set *unwrap_re2_set(VALUE self) {
|
|
@@ -2332,9 +2487,14 @@ static VALUE re2_set_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
2332
2487
|
parse_re2_options(&re2_options, options);
|
|
2333
2488
|
}
|
|
2334
2489
|
|
|
2490
|
+
rb_check_frozen(self);
|
|
2491
|
+
|
|
2492
|
+
/* Prevent re-initialisation: #match releases the GVL while holding a
|
|
2493
|
+
* pointer to s->set, so replacing s->set from another thread would free
|
|
2494
|
+
* the object out from under a concurrent match.
|
|
2495
|
+
*/
|
|
2335
2496
|
if (s->set) {
|
|
2336
|
-
|
|
2337
|
-
s->set = nullptr;
|
|
2497
|
+
rb_raise(rb_eTypeError, "already initialized RE2::Set");
|
|
2338
2498
|
}
|
|
2339
2499
|
|
|
2340
2500
|
s->set = new(std::nothrow) RE2::Set(re2_options, re2_anchor);
|
|
@@ -2362,6 +2522,7 @@ static VALUE re2_set_add(VALUE self, VALUE pattern) {
|
|
|
2362
2522
|
StringValue(pattern);
|
|
2363
2523
|
|
|
2364
2524
|
re2_set *s = unwrap_re2_set(self);
|
|
2525
|
+
rb_check_frozen(self);
|
|
2365
2526
|
|
|
2366
2527
|
int index;
|
|
2367
2528
|
VALUE msg;
|
|
@@ -2393,8 +2554,16 @@ static VALUE re2_set_add(VALUE self, VALUE pattern) {
|
|
|
2393
2554
|
*/
|
|
2394
2555
|
static VALUE re2_set_compile(VALUE self) {
|
|
2395
2556
|
re2_set *s = unwrap_re2_set(self);
|
|
2557
|
+
rb_check_frozen(self);
|
|
2558
|
+
|
|
2559
|
+
bool compiled = s->set->Compile();
|
|
2396
2560
|
|
|
2397
|
-
|
|
2561
|
+
if (compiled) {
|
|
2562
|
+
s->compiled = true;
|
|
2563
|
+
rb_obj_freeze(self);
|
|
2564
|
+
}
|
|
2565
|
+
|
|
2566
|
+
return BOOL2RUBY(compiled);
|
|
2398
2567
|
}
|
|
2399
2568
|
|
|
2400
2569
|
/*
|
|
@@ -2491,6 +2660,8 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2491
2660
|
rb_scan_args(argc, argv, "11", &str, &options);
|
|
2492
2661
|
|
|
2493
2662
|
StringValue(str);
|
|
2663
|
+
str = rb_str_new_frozen(str);
|
|
2664
|
+
|
|
2494
2665
|
re2_set *s = unwrap_re2_set(self);
|
|
2495
2666
|
|
|
2496
2667
|
if (RTEST(options)) {
|
|
@@ -2506,9 +2677,27 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2506
2677
|
|
|
2507
2678
|
if (raise_exception) {
|
|
2508
2679
|
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
2680
|
+
/* RE2::Set::Match dereferences a null program after a failed Compile. */
|
|
2681
|
+
if (!s->compiled) {
|
|
2682
|
+
rb_raise(re2_eSetMatchError, "#match must not be called before #compile");
|
|
2683
|
+
}
|
|
2684
|
+
|
|
2509
2685
|
RE2::Set::ErrorInfo e;
|
|
2510
|
-
|
|
2511
|
-
|
|
2686
|
+
nogvl_set_match_arg arg;
|
|
2687
|
+
arg.set = s->set;
|
|
2688
|
+
arg.text = re2::StringPiece(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
2689
|
+
arg.v = &v;
|
|
2690
|
+
arg.error_info = &e;
|
|
2691
|
+
arg.matched = false;
|
|
2692
|
+
|
|
2693
|
+
#ifdef _WIN32
|
|
2694
|
+
nogvl_set_match(&arg);
|
|
2695
|
+
#else
|
|
2696
|
+
rb_thread_call_without_gvl(nogvl_set_match, &arg, NULL, NULL);
|
|
2697
|
+
#endif
|
|
2698
|
+
RB_GC_GUARD(str);
|
|
2699
|
+
|
|
2700
|
+
bool match_failed = !arg.matched;
|
|
2512
2701
|
VALUE result = rb_ary_new2(v.size());
|
|
2513
2702
|
|
|
2514
2703
|
if (match_failed) {
|
|
@@ -2535,11 +2724,29 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2535
2724
|
rb_raise(re2_eSetUnsupportedError, "current version of RE2::Set::Match() does not output error information, :exception option can only be set to false");
|
|
2536
2725
|
#endif
|
|
2537
2726
|
} else {
|
|
2538
|
-
|
|
2539
|
-
|
|
2727
|
+
if (!s->compiled) {
|
|
2728
|
+
return rb_ary_new();
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
nogvl_set_match_arg arg;
|
|
2732
|
+
arg.set = s->set;
|
|
2733
|
+
arg.text = re2::StringPiece(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
2734
|
+
arg.v = &v;
|
|
2735
|
+
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
2736
|
+
arg.error_info = nullptr;
|
|
2737
|
+
#endif
|
|
2738
|
+
arg.matched = false;
|
|
2739
|
+
|
|
2740
|
+
#ifdef _WIN32
|
|
2741
|
+
nogvl_set_match(&arg);
|
|
2742
|
+
#else
|
|
2743
|
+
rb_thread_call_without_gvl(nogvl_set_match, &arg, NULL, NULL);
|
|
2744
|
+
#endif
|
|
2745
|
+
RB_GC_GUARD(str);
|
|
2746
|
+
|
|
2540
2747
|
VALUE result = rb_ary_new2(v.size());
|
|
2541
2748
|
|
|
2542
|
-
if (matched) {
|
|
2749
|
+
if (arg.matched) {
|
|
2543
2750
|
for (int index : v) {
|
|
2544
2751
|
rb_ary_push(result, INT2FIX(index));
|
|
2545
2752
|
}
|
|
@@ -2550,6 +2757,8 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2550
2757
|
}
|
|
2551
2758
|
|
|
2552
2759
|
extern "C" void Init_re2(void) {
|
|
2760
|
+
rb_ext_ractor_safe(true);
|
|
2761
|
+
|
|
2553
2762
|
re2_mRE2 = rb_define_module("RE2");
|
|
2554
2763
|
re2_cRegexp = rb_define_class_under(re2_mRE2, "Regexp", rb_cObject);
|
|
2555
2764
|
re2_eRegexpUnsupportedError = rb_define_class_under(re2_cRegexp,
|