re2 2.9.0 → 2.27.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 +6 -0
- data/README.md +130 -22
- data/Rakefile +52 -90
- data/dependencies.yml +4 -4
- data/ext/re2/extconf.rb +256 -290
- data/ext/re2/re2.cc +1037 -318
- data/ext/re2/recipes.rb +24 -21
- data/lib/re2/regexp.rb +2 -0
- data/lib/re2/scanner.rb +2 -0
- data/lib/re2/string.rb +8 -6
- data/lib/re2/version.rb +1 -1
- data/lib/re2.rb +2 -0
- data/ports/archives/20260107.1.tar.gz +0 -0
- data/ports/archives/re2-2025-11-05.tar.gz +0 -0
- data/re2.gemspec +6 -4
- data/spec/kernel_spec.rb +2 -0
- data/spec/re2/match_data_spec.rb +520 -5
- data/spec/re2/regexp_spec.rb +342 -0
- data/spec/re2/scanner_spec.rb +185 -15
- data/spec/re2/set_spec.rb +123 -6
- data/spec/re2/string_spec.rb +2 -0
- data/spec/re2_spec.rb +219 -43
- data/spec/spec_helper.rb +2 -0
- metadata +17 -21
- data/ports/archives/20240116.1.tar.gz +0 -0
- data/ports/archives/re2-2024-03-01.tar.gz +0 -0
data/ext/re2/re2.cc
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Released under the BSD Licence, please see LICENSE.txt
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
#include <
|
|
11
|
+
#include <cstdint>
|
|
12
12
|
|
|
13
13
|
#include <map>
|
|
14
14
|
#include <sstream>
|
|
@@ -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
|
|
|
@@ -43,6 +44,132 @@ typedef struct {
|
|
|
43
44
|
RE2::Set *set;
|
|
44
45
|
} re2_set;
|
|
45
46
|
|
|
47
|
+
struct nogvl_match_arg {
|
|
48
|
+
const RE2 *pattern;
|
|
49
|
+
re2::StringPiece text;
|
|
50
|
+
size_t startpos;
|
|
51
|
+
size_t endpos;
|
|
52
|
+
RE2::Anchor anchor;
|
|
53
|
+
re2::StringPiece *matches;
|
|
54
|
+
int n;
|
|
55
|
+
bool matched;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
static void *nogvl_match(void *ptr) {
|
|
59
|
+
auto *arg = static_cast<nogvl_match_arg *>(ptr);
|
|
60
|
+
#ifdef HAVE_ENDPOS_ARGUMENT
|
|
61
|
+
arg->matched = arg->pattern->Match(
|
|
62
|
+
arg->text, arg->startpos, arg->endpos,
|
|
63
|
+
arg->anchor, arg->matches, arg->n);
|
|
64
|
+
#else
|
|
65
|
+
arg->matched = arg->pattern->Match(
|
|
66
|
+
arg->text, arg->startpos,
|
|
67
|
+
arg->anchor, arg->matches, arg->n);
|
|
68
|
+
#endif
|
|
69
|
+
return nullptr;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static bool re2_match_without_gvl(
|
|
73
|
+
const RE2 *pattern, VALUE text, size_t startpos, size_t endpos,
|
|
74
|
+
RE2::Anchor anchor, re2::StringPiece *matches, int n) {
|
|
75
|
+
nogvl_match_arg arg;
|
|
76
|
+
arg.pattern = pattern;
|
|
77
|
+
arg.text = re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text));
|
|
78
|
+
arg.startpos = startpos;
|
|
79
|
+
arg.endpos = endpos;
|
|
80
|
+
arg.anchor = anchor;
|
|
81
|
+
arg.matches = matches;
|
|
82
|
+
arg.n = n;
|
|
83
|
+
arg.matched = false;
|
|
84
|
+
|
|
85
|
+
/* Abseil's synchronization primitives (SRWLOCK, SleepConditionVariableSRW)
|
|
86
|
+
* are incompatible with Ruby's Win32 Mutex-based GVL, causing
|
|
87
|
+
* WAIT_ABANDONED crashes when multiple threads match concurrently.
|
|
88
|
+
*/
|
|
89
|
+
#ifdef _WIN32
|
|
90
|
+
nogvl_match(&arg);
|
|
91
|
+
#else
|
|
92
|
+
/* No unblocking function is needed: RE2 matching is CPU-bound computation,
|
|
93
|
+
* not a blocking system call, so a signal cannot safely interrupt it.
|
|
94
|
+
*/
|
|
95
|
+
rb_thread_call_without_gvl(nogvl_match, &arg, NULL, NULL);
|
|
96
|
+
#endif
|
|
97
|
+
|
|
98
|
+
return arg.matched;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
struct nogvl_set_match_arg {
|
|
102
|
+
const RE2::Set *set;
|
|
103
|
+
re2::StringPiece text;
|
|
104
|
+
std::vector<int> *v;
|
|
105
|
+
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
106
|
+
RE2::Set::ErrorInfo *error_info;
|
|
107
|
+
#endif
|
|
108
|
+
bool matched;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
static void *nogvl_set_match(void *ptr) {
|
|
112
|
+
auto *arg = static_cast<nogvl_set_match_arg *>(ptr);
|
|
113
|
+
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
114
|
+
if (arg->error_info) {
|
|
115
|
+
arg->matched = arg->set->Match(arg->text, arg->v, arg->error_info);
|
|
116
|
+
} else {
|
|
117
|
+
arg->matched = arg->set->Match(arg->text, arg->v);
|
|
118
|
+
}
|
|
119
|
+
#else
|
|
120
|
+
arg->matched = arg->set->Match(arg->text, arg->v);
|
|
121
|
+
#endif
|
|
122
|
+
return nullptr;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
struct nogvl_replace_arg {
|
|
126
|
+
std::string *str;
|
|
127
|
+
const RE2 *pattern;
|
|
128
|
+
re2::StringPiece string_pattern;
|
|
129
|
+
re2::StringPiece rewrite;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
static void *nogvl_replace(void *ptr) {
|
|
133
|
+
auto *arg = static_cast<nogvl_replace_arg *>(ptr);
|
|
134
|
+
if (arg->pattern) {
|
|
135
|
+
RE2::Replace(arg->str, *arg->pattern, arg->rewrite);
|
|
136
|
+
} else {
|
|
137
|
+
RE2::Replace(arg->str, arg->string_pattern, arg->rewrite);
|
|
138
|
+
}
|
|
139
|
+
return nullptr;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static void *nogvl_global_replace(void *ptr) {
|
|
143
|
+
auto *arg = static_cast<nogvl_replace_arg *>(ptr);
|
|
144
|
+
if (arg->pattern) {
|
|
145
|
+
RE2::GlobalReplace(arg->str, *arg->pattern, arg->rewrite);
|
|
146
|
+
} else {
|
|
147
|
+
RE2::GlobalReplace(arg->str, arg->string_pattern, arg->rewrite);
|
|
148
|
+
}
|
|
149
|
+
return nullptr;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
struct nogvl_extract_arg {
|
|
153
|
+
re2::StringPiece text;
|
|
154
|
+
const RE2 *pattern;
|
|
155
|
+
re2::StringPiece string_pattern;
|
|
156
|
+
re2::StringPiece rewrite;
|
|
157
|
+
std::string *out;
|
|
158
|
+
bool extracted;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
static void *nogvl_extract(void *ptr) {
|
|
162
|
+
auto *arg = static_cast<nogvl_extract_arg *>(ptr);
|
|
163
|
+
if (arg->pattern) {
|
|
164
|
+
arg->extracted = RE2::Extract(arg->text, *arg->pattern,
|
|
165
|
+
arg->rewrite, arg->out);
|
|
166
|
+
} else {
|
|
167
|
+
arg->extracted = RE2::Extract(arg->text, RE2(arg->string_pattern),
|
|
168
|
+
arg->rewrite, arg->out);
|
|
169
|
+
}
|
|
170
|
+
return nullptr;
|
|
171
|
+
}
|
|
172
|
+
|
|
46
173
|
VALUE re2_mRE2, re2_cRegexp, re2_cMatchData, re2_cScanner, re2_cSet,
|
|
47
174
|
re2_eSetMatchError, re2_eSetUnsupportedError, re2_eRegexpUnsupportedError;
|
|
48
175
|
|
|
@@ -51,7 +178,7 @@ static ID id_utf8, id_posix_syntax, id_longest_match, id_log_errors,
|
|
|
51
178
|
id_max_mem, id_literal, id_never_nl, id_case_sensitive,
|
|
52
179
|
id_perl_classes, id_word_boundary, id_one_line, id_unanchored,
|
|
53
180
|
id_anchor, id_anchor_start, id_anchor_both, id_exception,
|
|
54
|
-
id_submatches, id_startpos, id_endpos;
|
|
181
|
+
id_submatches, id_startpos, id_endpos, id_symbolize_names;
|
|
55
182
|
|
|
56
183
|
inline VALUE encoded_str_new(const char *str, long length, RE2::Options::Encoding encoding) {
|
|
57
184
|
if (encoding == RE2::Options::EncodingUTF8) {
|
|
@@ -125,30 +252,23 @@ static void parse_re2_options(RE2::Options* re2_options, const VALUE options) {
|
|
|
125
252
|
}
|
|
126
253
|
}
|
|
127
254
|
|
|
128
|
-
/* For compatibility with Ruby < 2.7 */
|
|
129
|
-
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
130
|
-
#define re2_compact_callback(x) (x),
|
|
131
|
-
#else
|
|
132
|
-
#define rb_gc_mark_movable(x) rb_gc_mark(x)
|
|
133
|
-
#define re2_compact_callback(x)
|
|
134
|
-
#endif
|
|
135
|
-
|
|
136
255
|
static void re2_matchdata_mark(void *ptr) {
|
|
137
|
-
re2_matchdata *m =
|
|
256
|
+
re2_matchdata *m = static_cast<re2_matchdata *>(ptr);
|
|
138
257
|
rb_gc_mark_movable(m->regexp);
|
|
139
|
-
|
|
258
|
+
|
|
259
|
+
/* Text must not be movable because StringPiece matches hold pointers into
|
|
260
|
+
* its underlying buffer; moving the string would invalidate them.
|
|
261
|
+
*/
|
|
262
|
+
rb_gc_mark(m->text);
|
|
140
263
|
}
|
|
141
264
|
|
|
142
|
-
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
143
265
|
static void re2_matchdata_compact(void *ptr) {
|
|
144
|
-
re2_matchdata *m =
|
|
266
|
+
re2_matchdata *m = static_cast<re2_matchdata *>(ptr);
|
|
145
267
|
m->regexp = rb_gc_location(m->regexp);
|
|
146
|
-
m->text = rb_gc_location(m->text);
|
|
147
268
|
}
|
|
148
|
-
#endif
|
|
149
269
|
|
|
150
270
|
static void re2_matchdata_free(void *ptr) {
|
|
151
|
-
re2_matchdata *m =
|
|
271
|
+
re2_matchdata *m = static_cast<re2_matchdata *>(ptr);
|
|
152
272
|
if (m->matches) {
|
|
153
273
|
delete[] m->matches;
|
|
154
274
|
}
|
|
@@ -156,7 +276,7 @@ static void re2_matchdata_free(void *ptr) {
|
|
|
156
276
|
}
|
|
157
277
|
|
|
158
278
|
static size_t re2_matchdata_memsize(const void *ptr) {
|
|
159
|
-
const re2_matchdata *m =
|
|
279
|
+
const re2_matchdata *m = static_cast<const re2_matchdata *>(ptr);
|
|
160
280
|
size_t size = sizeof(*m);
|
|
161
281
|
if (m->matches) {
|
|
162
282
|
size += sizeof(*m->matches) * m->number_of_matches;
|
|
@@ -171,7 +291,7 @@ static const rb_data_type_t re2_matchdata_data_type = {
|
|
|
171
291
|
re2_matchdata_mark,
|
|
172
292
|
re2_matchdata_free,
|
|
173
293
|
re2_matchdata_memsize,
|
|
174
|
-
|
|
294
|
+
re2_matchdata_compact
|
|
175
295
|
},
|
|
176
296
|
0,
|
|
177
297
|
0,
|
|
@@ -181,21 +301,22 @@ static const rb_data_type_t re2_matchdata_data_type = {
|
|
|
181
301
|
};
|
|
182
302
|
|
|
183
303
|
static void re2_scanner_mark(void *ptr) {
|
|
184
|
-
re2_scanner *s =
|
|
304
|
+
re2_scanner *s = static_cast<re2_scanner *>(ptr);
|
|
185
305
|
rb_gc_mark_movable(s->regexp);
|
|
186
|
-
|
|
306
|
+
|
|
307
|
+
/* Text must not be movable because the StringPiece input holds a pointer
|
|
308
|
+
* into its underlying buffer; moving the string would invalidate it.
|
|
309
|
+
*/
|
|
310
|
+
rb_gc_mark(s->text);
|
|
187
311
|
}
|
|
188
312
|
|
|
189
|
-
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
|
190
313
|
static void re2_scanner_compact(void *ptr) {
|
|
191
|
-
re2_scanner *s =
|
|
314
|
+
re2_scanner *s = static_cast<re2_scanner *>(ptr);
|
|
192
315
|
s->regexp = rb_gc_location(s->regexp);
|
|
193
|
-
s->text = rb_gc_location(s->text);
|
|
194
316
|
}
|
|
195
|
-
#endif
|
|
196
317
|
|
|
197
318
|
static void re2_scanner_free(void *ptr) {
|
|
198
|
-
re2_scanner *s =
|
|
319
|
+
re2_scanner *s = static_cast<re2_scanner *>(ptr);
|
|
199
320
|
if (s->input) {
|
|
200
321
|
delete s->input;
|
|
201
322
|
}
|
|
@@ -203,7 +324,7 @@ static void re2_scanner_free(void *ptr) {
|
|
|
203
324
|
}
|
|
204
325
|
|
|
205
326
|
static size_t re2_scanner_memsize(const void *ptr) {
|
|
206
|
-
const re2_scanner *s =
|
|
327
|
+
const re2_scanner *s = static_cast<const re2_scanner *>(ptr);
|
|
207
328
|
size_t size = sizeof(*s);
|
|
208
329
|
if (s->input) {
|
|
209
330
|
size += sizeof(*s->input);
|
|
@@ -218,7 +339,7 @@ static const rb_data_type_t re2_scanner_data_type = {
|
|
|
218
339
|
re2_scanner_mark,
|
|
219
340
|
re2_scanner_free,
|
|
220
341
|
re2_scanner_memsize,
|
|
221
|
-
|
|
342
|
+
re2_scanner_compact
|
|
222
343
|
},
|
|
223
344
|
0,
|
|
224
345
|
0,
|
|
@@ -228,7 +349,7 @@ static const rb_data_type_t re2_scanner_data_type = {
|
|
|
228
349
|
};
|
|
229
350
|
|
|
230
351
|
static void re2_regexp_free(void *ptr) {
|
|
231
|
-
re2_pattern *p =
|
|
352
|
+
re2_pattern *p = static_cast<re2_pattern *>(ptr);
|
|
232
353
|
if (p->pattern) {
|
|
233
354
|
delete p->pattern;
|
|
234
355
|
}
|
|
@@ -236,7 +357,7 @@ static void re2_regexp_free(void *ptr) {
|
|
|
236
357
|
}
|
|
237
358
|
|
|
238
359
|
static size_t re2_regexp_memsize(const void *ptr) {
|
|
239
|
-
const re2_pattern *p =
|
|
360
|
+
const re2_pattern *p = static_cast<const re2_pattern *>(ptr);
|
|
240
361
|
size_t size = sizeof(*p);
|
|
241
362
|
if (p->pattern) {
|
|
242
363
|
size += sizeof(*p->pattern);
|
|
@@ -256,9 +377,64 @@ static const rb_data_type_t re2_regexp_data_type = {
|
|
|
256
377
|
0,
|
|
257
378
|
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
|
|
258
379
|
// macro to update VALUE references, as to trigger write barriers.
|
|
259
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
|
380
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE
|
|
260
381
|
};
|
|
261
382
|
|
|
383
|
+
static re2_pattern *unwrap_re2_regexp(VALUE self) {
|
|
384
|
+
re2_pattern *p;
|
|
385
|
+
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
386
|
+
if (!p->pattern) {
|
|
387
|
+
rb_raise(rb_eTypeError, "uninitialized RE2::Regexp");
|
|
388
|
+
}
|
|
389
|
+
return p;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static re2_matchdata *unwrap_re2_matchdata(VALUE self) {
|
|
393
|
+
re2_matchdata *m;
|
|
394
|
+
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
395
|
+
if (!RTEST(m->regexp)) {
|
|
396
|
+
rb_raise(rb_eTypeError, "uninitialized RE2::MatchData");
|
|
397
|
+
}
|
|
398
|
+
return m;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
static re2_scanner *unwrap_re2_scanner(VALUE self) {
|
|
402
|
+
re2_scanner *c;
|
|
403
|
+
TypedData_Get_Struct(self, re2_scanner, &re2_scanner_data_type, c);
|
|
404
|
+
if (!RTEST(c->regexp)) {
|
|
405
|
+
rb_raise(rb_eTypeError, "uninitialized RE2::Scanner");
|
|
406
|
+
}
|
|
407
|
+
return c;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/*
|
|
411
|
+
* Returns an array of names of all named capturing groups. Names are returned
|
|
412
|
+
* in alphabetical order rather than definition order, as RE2 stores named
|
|
413
|
+
* groups internally in a sorted map.
|
|
414
|
+
*
|
|
415
|
+
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
416
|
+
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
417
|
+
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
418
|
+
*
|
|
419
|
+
* @return [Array<String>] an array of names of named capturing groups
|
|
420
|
+
* @example
|
|
421
|
+
* RE2::Regexp.new('(?P<a>\d+) (?P<b>\w+)').names #=> ["a", "b"]
|
|
422
|
+
*/
|
|
423
|
+
static VALUE re2_regexp_names(const VALUE self) {
|
|
424
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
425
|
+
|
|
426
|
+
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
427
|
+
VALUE names = rb_ary_new2(groups.size());
|
|
428
|
+
|
|
429
|
+
for (const auto& group : groups) {
|
|
430
|
+
rb_ary_push(names,
|
|
431
|
+
encoded_str_new(group.first.data(), group.first.size(),
|
|
432
|
+
p->pattern->options().encoding()));
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return names;
|
|
436
|
+
}
|
|
437
|
+
|
|
262
438
|
static VALUE re2_matchdata_allocate(VALUE klass) {
|
|
263
439
|
re2_matchdata *m;
|
|
264
440
|
|
|
@@ -283,24 +459,24 @@ static VALUE re2_scanner_allocate(VALUE klass) {
|
|
|
283
459
|
* m.string #=> "bob 123"
|
|
284
460
|
*/
|
|
285
461
|
static VALUE re2_matchdata_string(const VALUE self) {
|
|
286
|
-
re2_matchdata *m;
|
|
287
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
462
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
288
463
|
|
|
289
464
|
return m->text;
|
|
290
465
|
}
|
|
291
466
|
|
|
292
467
|
/*
|
|
293
|
-
* Returns the text supplied when incrementally matching with
|
|
468
|
+
* Returns a frozen copy of the text supplied when incrementally matching with
|
|
294
469
|
* {RE2::Regexp#scan}.
|
|
295
470
|
*
|
|
296
|
-
*
|
|
471
|
+
* If the text was already a frozen string, returns the original.
|
|
472
|
+
*
|
|
473
|
+
* @return [String] a frozen string with the text passed to {RE2::Regexp#scan}
|
|
297
474
|
* @example
|
|
298
475
|
* c = RE2::Regexp.new('(\d+)').scan("foo")
|
|
299
476
|
* c.string #=> "foo"
|
|
300
477
|
*/
|
|
301
478
|
static VALUE re2_scanner_string(const VALUE self) {
|
|
302
|
-
re2_scanner *c;
|
|
303
|
-
TypedData_Get_Struct(self, re2_scanner, &re2_scanner_data_type, c);
|
|
479
|
+
re2_scanner *c = unwrap_re2_scanner(self);
|
|
304
480
|
|
|
305
481
|
return c->text;
|
|
306
482
|
}
|
|
@@ -314,8 +490,7 @@ static VALUE re2_scanner_string(const VALUE self) {
|
|
|
314
490
|
* c.eof? #=> true
|
|
315
491
|
*/
|
|
316
492
|
static VALUE re2_scanner_eof(const VALUE self) {
|
|
317
|
-
re2_scanner *c;
|
|
318
|
-
TypedData_Get_Struct(self, re2_scanner, &re2_scanner_data_type, c);
|
|
493
|
+
re2_scanner *c = unwrap_re2_scanner(self);
|
|
319
494
|
|
|
320
495
|
return BOOL2RUBY(c->eof);
|
|
321
496
|
}
|
|
@@ -332,17 +507,50 @@ static VALUE re2_scanner_eof(const VALUE self) {
|
|
|
332
507
|
* e.scan #=> ["1"]
|
|
333
508
|
*/
|
|
334
509
|
static VALUE re2_scanner_rewind(VALUE self) {
|
|
335
|
-
re2_scanner *c;
|
|
336
|
-
TypedData_Get_Struct(self, re2_scanner, &re2_scanner_data_type, c);
|
|
510
|
+
re2_scanner *c = unwrap_re2_scanner(self);
|
|
337
511
|
|
|
338
512
|
delete c->input;
|
|
339
513
|
c->input = new(std::nothrow) re2::StringPiece(
|
|
340
514
|
RSTRING_PTR(c->text), RSTRING_LEN(c->text));
|
|
515
|
+
if (c->input == nullptr) {
|
|
516
|
+
rb_raise(rb_eNoMemError,
|
|
517
|
+
"not enough memory to allocate StringPiece for input");
|
|
518
|
+
}
|
|
519
|
+
|
|
341
520
|
c->eof = false;
|
|
342
521
|
|
|
343
522
|
return self;
|
|
344
523
|
}
|
|
345
524
|
|
|
525
|
+
static VALUE re2_scanner_initialize_copy(VALUE self, VALUE other) {
|
|
526
|
+
re2_scanner *self_c;
|
|
527
|
+
re2_scanner *other_c = unwrap_re2_scanner(other);
|
|
528
|
+
|
|
529
|
+
TypedData_Get_Struct(self, re2_scanner, &re2_scanner_data_type, self_c);
|
|
530
|
+
|
|
531
|
+
if (self_c->input) {
|
|
532
|
+
delete self_c->input;
|
|
533
|
+
self_c->input = nullptr;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
RB_OBJ_WRITE(self, &self_c->regexp, other_c->regexp);
|
|
537
|
+
RB_OBJ_WRITE(self, &self_c->text, other_c->text);
|
|
538
|
+
self_c->number_of_capturing_groups = other_c->number_of_capturing_groups;
|
|
539
|
+
self_c->eof = other_c->eof;
|
|
540
|
+
|
|
541
|
+
if (other_c->input) {
|
|
542
|
+
self_c->input = new(std::nothrow) re2::StringPiece(*other_c->input);
|
|
543
|
+
if (self_c->input == nullptr) {
|
|
544
|
+
rb_raise(rb_eNoMemError,
|
|
545
|
+
"not enough memory to allocate StringPiece for input");
|
|
546
|
+
}
|
|
547
|
+
} else {
|
|
548
|
+
self_c->input = nullptr;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return self;
|
|
552
|
+
}
|
|
553
|
+
|
|
346
554
|
/*
|
|
347
555
|
* Scan the given text incrementally for matches using
|
|
348
556
|
* {https://github.com/google/re2/blob/bc0faab533e2b27b85b8ad312abf061e33ed6b5d/re2/re2.h#L447-L463
|
|
@@ -363,11 +571,8 @@ static VALUE re2_scanner_rewind(VALUE self) {
|
|
|
363
571
|
* s.scan #=> ["bar"]
|
|
364
572
|
*/
|
|
365
573
|
static VALUE re2_scanner_scan(VALUE self) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
TypedData_Get_Struct(self, re2_scanner, &re2_scanner_data_type, c);
|
|
370
|
-
TypedData_Get_Struct(c->regexp, re2_pattern, &re2_regexp_data_type, p);
|
|
574
|
+
re2_scanner *c = unwrap_re2_scanner(self);
|
|
575
|
+
re2_pattern *p = unwrap_re2_regexp(c->regexp);
|
|
371
576
|
|
|
372
577
|
std::vector<RE2::Arg> argv(c->number_of_capturing_groups);
|
|
373
578
|
std::vector<RE2::Arg*> args(c->number_of_capturing_groups);
|
|
@@ -392,7 +597,7 @@ static VALUE re2_scanner_scan(VALUE self) {
|
|
|
392
597
|
VALUE result = rb_ary_new2(c->number_of_capturing_groups);
|
|
393
598
|
|
|
394
599
|
for (int i = 0; i < c->number_of_capturing_groups; ++i) {
|
|
395
|
-
if (matches[i].
|
|
600
|
+
if (matches[i].data() == nullptr) {
|
|
396
601
|
rb_ary_push(result, Qnil);
|
|
397
602
|
} else {
|
|
398
603
|
rb_ary_push(result, encoded_str_new(matches[i].data(),
|
|
@@ -404,9 +609,27 @@ static VALUE re2_scanner_scan(VALUE self) {
|
|
|
404
609
|
/* Check whether we've exhausted the input yet. */
|
|
405
610
|
c->eof = new_input_size == 0;
|
|
406
611
|
|
|
407
|
-
/* If the match didn't advance the input, we need to do this ourselves
|
|
612
|
+
/* If the match didn't advance the input, we need to do this ourselves,
|
|
613
|
+
* advancing by a whole character to avoid splitting multi-byte characters.
|
|
614
|
+
*
|
|
615
|
+
* The lookup table approach is taken from RE2's own Python extension: the
|
|
616
|
+
* high 4 bits of a UTF-8 lead byte determine the character's byte length.
|
|
617
|
+
*
|
|
618
|
+
* See https://github.com/google/re2/blob/972a15cedd008d846f1a39b2e88ce48d7f166cbd/python/_re2.cc#L46-L48
|
|
619
|
+
*/
|
|
408
620
|
if (!input_advanced && new_input_size > 0) {
|
|
409
|
-
|
|
621
|
+
size_t char_size = 1;
|
|
622
|
+
|
|
623
|
+
if (p->pattern->options().encoding() == RE2::Options::EncodingUTF8) {
|
|
624
|
+
char_size = "\1\1\1\1\1\1\1\1\1\1\1\1\2\2\3\4"
|
|
625
|
+
[((*c->input)[0] & 0xFF) >> 4];
|
|
626
|
+
|
|
627
|
+
if (char_size > new_input_size) {
|
|
628
|
+
char_size = new_input_size;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
c->input->remove_prefix(char_size);
|
|
410
633
|
}
|
|
411
634
|
|
|
412
635
|
return result;
|
|
@@ -416,47 +639,44 @@ static VALUE re2_scanner_scan(VALUE self) {
|
|
|
416
639
|
}
|
|
417
640
|
|
|
418
641
|
static re2::StringPiece *re2_matchdata_find_match(VALUE idx, const VALUE self) {
|
|
419
|
-
re2_matchdata *m;
|
|
420
|
-
re2_pattern *p;
|
|
421
|
-
|
|
422
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
423
|
-
TypedData_Get_Struct(m->regexp, re2_pattern, &re2_regexp_data_type, p);
|
|
642
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
643
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
424
644
|
|
|
425
645
|
int id;
|
|
426
646
|
|
|
427
|
-
if (
|
|
428
|
-
id =
|
|
647
|
+
if (RB_INTEGER_TYPE_P(idx)) {
|
|
648
|
+
id = NUM2INT(idx);
|
|
429
649
|
} else if (SYMBOL_P(idx)) {
|
|
430
|
-
const
|
|
431
|
-
|
|
650
|
+
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
651
|
+
auto search = groups.find(rb_id2name(SYM2ID(idx)));
|
|
432
652
|
|
|
433
653
|
if (search != groups.end()) {
|
|
434
654
|
id = search->second;
|
|
435
655
|
} else {
|
|
436
|
-
return
|
|
656
|
+
return nullptr;
|
|
437
657
|
}
|
|
438
658
|
} else {
|
|
439
659
|
StringValue(idx);
|
|
440
660
|
|
|
441
|
-
const
|
|
442
|
-
|
|
661
|
+
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
662
|
+
auto search = groups.find(std::string(RSTRING_PTR(idx), RSTRING_LEN(idx)));
|
|
443
663
|
|
|
444
664
|
if (search != groups.end()) {
|
|
445
665
|
id = search->second;
|
|
446
666
|
} else {
|
|
447
|
-
return
|
|
667
|
+
return nullptr;
|
|
448
668
|
}
|
|
449
669
|
}
|
|
450
670
|
|
|
451
671
|
if (id >= 0 && id < m->number_of_matches) {
|
|
452
672
|
re2::StringPiece *match = &m->matches[id];
|
|
453
673
|
|
|
454
|
-
if (
|
|
674
|
+
if (match->data() != nullptr) {
|
|
455
675
|
return match;
|
|
456
676
|
}
|
|
457
677
|
}
|
|
458
678
|
|
|
459
|
-
return
|
|
679
|
+
return nullptr;
|
|
460
680
|
}
|
|
461
681
|
|
|
462
682
|
/*
|
|
@@ -465,14 +685,12 @@ static re2::StringPiece *re2_matchdata_find_match(VALUE idx, const VALUE self) {
|
|
|
465
685
|
*
|
|
466
686
|
* @return [Integer] the number of elements
|
|
467
687
|
* @example
|
|
468
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
688
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
469
689
|
* m.size #=> 2
|
|
470
690
|
* m.length #=> 2
|
|
471
691
|
*/
|
|
472
692
|
static VALUE re2_matchdata_size(const VALUE self) {
|
|
473
|
-
re2_matchdata *m;
|
|
474
|
-
|
|
475
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
693
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
476
694
|
|
|
477
695
|
return INT2FIX(m->number_of_matches);
|
|
478
696
|
}
|
|
@@ -484,17 +702,15 @@ static VALUE re2_matchdata_size(const VALUE self) {
|
|
|
484
702
|
* @return [Integer, nil] the offset of the start of the match or `nil` if
|
|
485
703
|
* there is no such submatch
|
|
486
704
|
* @example
|
|
487
|
-
* m = RE2::Regexp.new('ob (\d+)').
|
|
705
|
+
* m = RE2::Regexp.new('ob (\d+)').partial_match("bob 123")
|
|
488
706
|
* m.begin(0) #=> 1
|
|
489
707
|
* m.begin(1) #=> 4
|
|
490
708
|
*/
|
|
491
709
|
static VALUE re2_matchdata_begin(const VALUE self, VALUE n) {
|
|
492
|
-
re2_matchdata *m;
|
|
493
|
-
|
|
494
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
710
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
495
711
|
|
|
496
712
|
re2::StringPiece *match = re2_matchdata_find_match(n, self);
|
|
497
|
-
if (match ==
|
|
713
|
+
if (match == nullptr) {
|
|
498
714
|
return Qnil;
|
|
499
715
|
} else {
|
|
500
716
|
long offset = match->data() - RSTRING_PTR(m->text);
|
|
@@ -511,17 +727,15 @@ static VALUE re2_matchdata_begin(const VALUE self, VALUE n) {
|
|
|
511
727
|
* @return [Integer, nil] the offset of the character following the end of the
|
|
512
728
|
* match or `nil` if there is no such match
|
|
513
729
|
* @example
|
|
514
|
-
* m = RE2::Regexp.new('ob (\d+) b').
|
|
730
|
+
* m = RE2::Regexp.new('ob (\d+) b').partial_match("bob 123 bob")
|
|
515
731
|
* m.end(0) #=> 9
|
|
516
732
|
* m.end(1) #=> 7
|
|
517
733
|
*/
|
|
518
734
|
static VALUE re2_matchdata_end(const VALUE self, VALUE n) {
|
|
519
|
-
re2_matchdata *m;
|
|
520
|
-
|
|
521
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
735
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
522
736
|
|
|
523
737
|
re2::StringPiece *match = re2_matchdata_find_match(n, self);
|
|
524
|
-
if (match ==
|
|
738
|
+
if (match == nullptr) {
|
|
525
739
|
return Qnil;
|
|
526
740
|
} else {
|
|
527
741
|
long offset = (match->data() - RSTRING_PTR(m->text)) + match->size();
|
|
@@ -530,17 +744,129 @@ static VALUE re2_matchdata_end(const VALUE self, VALUE n) {
|
|
|
530
744
|
}
|
|
531
745
|
}
|
|
532
746
|
|
|
747
|
+
/*
|
|
748
|
+
* Returns the portion of the original string before the match.
|
|
749
|
+
*
|
|
750
|
+
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
751
|
+
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
752
|
+
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
753
|
+
*
|
|
754
|
+
* @return [String] the portion of the original string before the match
|
|
755
|
+
* @example
|
|
756
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123 456")
|
|
757
|
+
* m.pre_match #=> "bob "
|
|
758
|
+
*/
|
|
759
|
+
static VALUE re2_matchdata_pre_match(const VALUE self) {
|
|
760
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
761
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
762
|
+
|
|
763
|
+
re2::StringPiece *match = &m->matches[0];
|
|
764
|
+
if (match->data() == nullptr) {
|
|
765
|
+
return Qnil;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
long offset = match->data() - RSTRING_PTR(m->text);
|
|
769
|
+
|
|
770
|
+
return encoded_str_new(RSTRING_PTR(m->text), offset,
|
|
771
|
+
p->pattern->options().encoding());
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/*
|
|
775
|
+
* Returns the portion of the original string after the match.
|
|
776
|
+
*
|
|
777
|
+
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
778
|
+
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
779
|
+
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
780
|
+
*
|
|
781
|
+
* @return [String] the portion of the original string after the match
|
|
782
|
+
* @example
|
|
783
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123 456")
|
|
784
|
+
* m.post_match #=> " 456"
|
|
785
|
+
*/
|
|
786
|
+
static VALUE re2_matchdata_post_match(const VALUE self) {
|
|
787
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
788
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
789
|
+
|
|
790
|
+
re2::StringPiece *match = &m->matches[0];
|
|
791
|
+
if (match->data() == nullptr) {
|
|
792
|
+
return Qnil;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
long start = (match->data() - RSTRING_PTR(m->text)) + match->size();
|
|
796
|
+
long remaining = RSTRING_LEN(m->text) - start;
|
|
797
|
+
|
|
798
|
+
return encoded_str_new(RSTRING_PTR(m->text) + start, remaining,
|
|
799
|
+
p->pattern->options().encoding());
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
/*
|
|
803
|
+
* Returns a two-element array containing the beginning and ending offsets of
|
|
804
|
+
* the nth match.
|
|
805
|
+
*
|
|
806
|
+
* @param [Integer, String, Symbol] n the name or number of the match
|
|
807
|
+
* @return [Array<Integer>, nil] a two-element array with the beginning and
|
|
808
|
+
* ending offsets of the match or `nil` if there is no such match
|
|
809
|
+
* @example
|
|
810
|
+
* m = RE2::Regexp.new('ob (\d+)').partial_match("bob 123")
|
|
811
|
+
* m.offset(0) #=> [1, 7]
|
|
812
|
+
* m.offset(1) #=> [4, 7]
|
|
813
|
+
*/
|
|
814
|
+
static VALUE re2_matchdata_offset(const VALUE self, VALUE n) {
|
|
815
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
816
|
+
|
|
817
|
+
re2::StringPiece *match = re2_matchdata_find_match(n, self);
|
|
818
|
+
if (match == nullptr) {
|
|
819
|
+
return Qnil;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
long start = match->data() - RSTRING_PTR(m->text);
|
|
823
|
+
long end_pos = start + match->size();
|
|
824
|
+
|
|
825
|
+
VALUE array = rb_ary_new2(2);
|
|
826
|
+
rb_ary_push(array, LONG2NUM(rb_str_sublen(m->text, start)));
|
|
827
|
+
rb_ary_push(array, LONG2NUM(rb_str_sublen(m->text, end_pos)));
|
|
828
|
+
|
|
829
|
+
return array;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/*
|
|
833
|
+
* Returns the length of the nth match in characters. This is equivalent to
|
|
834
|
+
* `m[n].length` but without allocating a new string.
|
|
835
|
+
*
|
|
836
|
+
* @param [Integer, String, Symbol] n the name or number of the match
|
|
837
|
+
* @return [Integer, nil] the length of the match or `nil` if there is no such
|
|
838
|
+
* match
|
|
839
|
+
* @example
|
|
840
|
+
* m = RE2::Regexp.new('(?P<word>\w+) (?P<number>\d+)').partial_match("alice 123")
|
|
841
|
+
* m.match_length(0) #=> 9
|
|
842
|
+
* m.match_length(1) #=> 5
|
|
843
|
+
* m.match_length(:number) #=> 3
|
|
844
|
+
*/
|
|
845
|
+
static VALUE re2_matchdata_match_length(const VALUE self, VALUE n) {
|
|
846
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
847
|
+
|
|
848
|
+
re2::StringPiece *match = re2_matchdata_find_match(n, self);
|
|
849
|
+
if (match == nullptr) {
|
|
850
|
+
return Qnil;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
long start = match->data() - RSTRING_PTR(m->text);
|
|
854
|
+
long end_pos = start + match->size();
|
|
855
|
+
long char_len = rb_str_sublen(m->text, end_pos) - rb_str_sublen(m->text, start);
|
|
856
|
+
|
|
857
|
+
return LONG2NUM(char_len);
|
|
858
|
+
}
|
|
859
|
+
|
|
533
860
|
/*
|
|
534
861
|
* Returns the {RE2::Regexp} used in the match.
|
|
535
862
|
*
|
|
536
863
|
* @return [RE2::Regexp] the regular expression used in the match
|
|
537
864
|
* @example
|
|
538
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
865
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
539
866
|
* m.regexp #=> #<RE2::Regexp /(\d+)/>
|
|
540
867
|
*/
|
|
541
868
|
static VALUE re2_matchdata_regexp(const VALUE self) {
|
|
542
|
-
re2_matchdata *m;
|
|
543
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
869
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
544
870
|
|
|
545
871
|
return m->regexp;
|
|
546
872
|
}
|
|
@@ -554,8 +880,7 @@ static VALUE re2_matchdata_regexp(const VALUE self) {
|
|
|
554
880
|
* c.regexp #=> #<RE2::Regexp /(\d+)/>
|
|
555
881
|
*/
|
|
556
882
|
static VALUE re2_scanner_regexp(const VALUE self) {
|
|
557
|
-
re2_scanner *c;
|
|
558
|
-
TypedData_Get_Struct(self, re2_scanner, &re2_scanner_data_type, c);
|
|
883
|
+
re2_scanner *c = unwrap_re2_scanner(self);
|
|
559
884
|
|
|
560
885
|
return c->regexp;
|
|
561
886
|
}
|
|
@@ -576,21 +901,18 @@ static VALUE re2_regexp_allocate(VALUE klass) {
|
|
|
576
901
|
*
|
|
577
902
|
* @return [Array<String, nil>] the array of matches
|
|
578
903
|
* @example
|
|
579
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
904
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
580
905
|
* m.to_a #=> ["123", "123"]
|
|
581
906
|
*/
|
|
582
907
|
static VALUE re2_matchdata_to_a(const VALUE self) {
|
|
583
|
-
re2_matchdata *m;
|
|
584
|
-
re2_pattern *p;
|
|
585
|
-
|
|
586
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
587
|
-
TypedData_Get_Struct(m->regexp, re2_pattern, &re2_regexp_data_type, p);
|
|
908
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
909
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
588
910
|
|
|
589
911
|
VALUE array = rb_ary_new2(m->number_of_matches);
|
|
590
912
|
for (int i = 0; i < m->number_of_matches; ++i) {
|
|
591
913
|
re2::StringPiece *match = &m->matches[i];
|
|
592
914
|
|
|
593
|
-
if (match->
|
|
915
|
+
if (match->data() == nullptr) {
|
|
594
916
|
rb_ary_push(array, Qnil);
|
|
595
917
|
} else {
|
|
596
918
|
rb_ary_push(array, encoded_str_new(match->data(), match->size(),
|
|
@@ -602,18 +924,15 @@ static VALUE re2_matchdata_to_a(const VALUE self) {
|
|
|
602
924
|
}
|
|
603
925
|
|
|
604
926
|
static VALUE re2_matchdata_nth_match(int nth, const VALUE self) {
|
|
605
|
-
re2_matchdata *m;
|
|
606
|
-
re2_pattern *p;
|
|
607
|
-
|
|
608
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
609
|
-
TypedData_Get_Struct(m->regexp, re2_pattern, &re2_regexp_data_type, p);
|
|
927
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
928
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
610
929
|
|
|
611
930
|
if (nth < 0 || nth >= m->number_of_matches) {
|
|
612
931
|
return Qnil;
|
|
613
932
|
} else {
|
|
614
933
|
re2::StringPiece *match = &m->matches[nth];
|
|
615
934
|
|
|
616
|
-
if (match->
|
|
935
|
+
if (match->data() == nullptr) {
|
|
617
936
|
return Qnil;
|
|
618
937
|
} else {
|
|
619
938
|
return encoded_str_new(match->data(), match->size(),
|
|
@@ -623,14 +942,11 @@ static VALUE re2_matchdata_nth_match(int nth, const VALUE self) {
|
|
|
623
942
|
}
|
|
624
943
|
|
|
625
944
|
static VALUE re2_matchdata_named_match(const std::string &name, const VALUE self) {
|
|
626
|
-
re2_matchdata *m;
|
|
627
|
-
re2_pattern *p;
|
|
945
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
946
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
628
947
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const std::map<std::string, int>& groups = p->pattern->NamedCapturingGroups();
|
|
633
|
-
std::map<std::string, int>::const_iterator search = groups.find(name);
|
|
948
|
+
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
949
|
+
auto search = groups.find(name);
|
|
634
950
|
|
|
635
951
|
if (search != groups.end()) {
|
|
636
952
|
return re2_matchdata_nth_match(search->second, self);
|
|
@@ -652,7 +968,7 @@ static VALUE re2_matchdata_named_match(const std::string &name, const VALUE self
|
|
|
652
968
|
* @param [Integer] index the index of the match to fetch
|
|
653
969
|
* @return [String, nil] the specified match or `nil` if it isn't present
|
|
654
970
|
* @example
|
|
655
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
971
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
656
972
|
* m[0] #=> "123"
|
|
657
973
|
*
|
|
658
974
|
* @overload [](start, length)
|
|
@@ -662,7 +978,7 @@ static VALUE re2_matchdata_named_match(const std::string &name, const VALUE self
|
|
|
662
978
|
* @param [Integer] length the number of elements to fetch
|
|
663
979
|
* @return [Array<String, nil>] the specified matches
|
|
664
980
|
* @example
|
|
665
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
981
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
666
982
|
* m[0, 1] #=> ["123"]
|
|
667
983
|
*
|
|
668
984
|
* @overload [](range)
|
|
@@ -671,8 +987,8 @@ static VALUE re2_matchdata_named_match(const std::string &name, const VALUE self
|
|
|
671
987
|
* @param [Range] range the range of match indexes to fetch
|
|
672
988
|
* @return [Array<String, nil>] the specified matches
|
|
673
989
|
* @example
|
|
674
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
675
|
-
* m[0..1] #=> "
|
|
990
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
991
|
+
* m[0..1] #=> ["123", "123"]
|
|
676
992
|
*
|
|
677
993
|
* @overload [](name)
|
|
678
994
|
* Access a particular match by name.
|
|
@@ -680,7 +996,7 @@ static VALUE re2_matchdata_named_match(const std::string &name, const VALUE self
|
|
|
680
996
|
* @param [String, Symbol] name the name of the match to fetch
|
|
681
997
|
* @return [String, nil] the specific match or `nil` if it isn't present
|
|
682
998
|
* @example
|
|
683
|
-
* m = RE2::Regexp.new('(?P<number>\d+)').
|
|
999
|
+
* m = RE2::Regexp.new('(?P<number>\d+)').partial_match("bob 123")
|
|
684
1000
|
* m["number"] #=> "123"
|
|
685
1001
|
* m[:number] #=> "123"
|
|
686
1002
|
*/
|
|
@@ -693,10 +1009,10 @@ static VALUE re2_matchdata_aref(int argc, VALUE *argv, const VALUE self) {
|
|
|
693
1009
|
std::string(RSTRING_PTR(idx), RSTRING_LEN(idx)), self);
|
|
694
1010
|
} else if (SYMBOL_P(idx)) {
|
|
695
1011
|
return re2_matchdata_named_match(rb_id2name(SYM2ID(idx)), self);
|
|
696
|
-
} else if (!NIL_P(rest) || !
|
|
1012
|
+
} else if (!NIL_P(rest) || !RB_INTEGER_TYPE_P(idx) || NUM2INT(idx) < 0) {
|
|
697
1013
|
return rb_ary_aref(argc, argv, re2_matchdata_to_a(self));
|
|
698
1014
|
} else {
|
|
699
|
-
return re2_matchdata_nth_match(
|
|
1015
|
+
return re2_matchdata_nth_match(NUM2INT(idx), self);
|
|
700
1016
|
}
|
|
701
1017
|
}
|
|
702
1018
|
|
|
@@ -704,6 +1020,9 @@ static VALUE re2_matchdata_aref(int argc, VALUE *argv, const VALUE self) {
|
|
|
704
1020
|
* Returns the entire matched string.
|
|
705
1021
|
*
|
|
706
1022
|
* @return [String] the entire matched string
|
|
1023
|
+
* @example
|
|
1024
|
+
* m = RE2::Regexp.new('(?P<number>\d+)').partial_match("bob 123")
|
|
1025
|
+
* m.to_s #=> "123"
|
|
707
1026
|
*/
|
|
708
1027
|
static VALUE re2_matchdata_to_s(const VALUE self) {
|
|
709
1028
|
return re2_matchdata_nth_match(0, self);
|
|
@@ -718,15 +1037,12 @@ static VALUE re2_matchdata_to_s(const VALUE self) {
|
|
|
718
1037
|
*
|
|
719
1038
|
* @return [String] a printable version of the match
|
|
720
1039
|
* @example
|
|
721
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
1040
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
722
1041
|
* m.inspect #=> "#<RE2::MatchData \"123\" 1:\"123\">"
|
|
723
1042
|
*/
|
|
724
1043
|
static VALUE re2_matchdata_inspect(const VALUE self) {
|
|
725
|
-
re2_matchdata *m;
|
|
726
|
-
re2_pattern *p;
|
|
727
|
-
|
|
728
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
729
|
-
TypedData_Get_Struct(m->regexp, re2_pattern, &re2_regexp_data_type, p);
|
|
1044
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
1045
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
730
1046
|
|
|
731
1047
|
std::ostringstream output;
|
|
732
1048
|
output << "#<RE2::MatchData";
|
|
@@ -756,7 +1072,7 @@ static VALUE re2_matchdata_inspect(const VALUE self) {
|
|
|
756
1072
|
}
|
|
757
1073
|
|
|
758
1074
|
/*
|
|
759
|
-
* Returns the array of submatches
|
|
1075
|
+
* Returns the array of submatches.
|
|
760
1076
|
*
|
|
761
1077
|
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
762
1078
|
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
@@ -765,11 +1081,12 @@ static VALUE re2_matchdata_inspect(const VALUE self) {
|
|
|
765
1081
|
*
|
|
766
1082
|
* @return [Array<String, nil>] the array of submatches
|
|
767
1083
|
* @example
|
|
768
|
-
* m = RE2::Regexp.new('(\d+)').
|
|
1084
|
+
* m = RE2::Regexp.new('(\d+)').partial_match("bob 123")
|
|
1085
|
+
* m.captures #=> ["123"]
|
|
769
1086
|
* m.deconstruct #=> ["123"]
|
|
770
1087
|
*
|
|
771
1088
|
* @example pattern matching
|
|
772
|
-
* case RE2::Regexp.new('(\d+) (\d+)').
|
|
1089
|
+
* case RE2::Regexp.new('(\d+) (\d+)').partial_match("bob 123 456")
|
|
773
1090
|
* in x, y
|
|
774
1091
|
* puts "Matched #{x} #{y}"
|
|
775
1092
|
* else
|
|
@@ -777,17 +1094,14 @@ static VALUE re2_matchdata_inspect(const VALUE self) {
|
|
|
777
1094
|
* end
|
|
778
1095
|
*/
|
|
779
1096
|
static VALUE re2_matchdata_deconstruct(const VALUE self) {
|
|
780
|
-
re2_matchdata *m;
|
|
781
|
-
re2_pattern *p;
|
|
782
|
-
|
|
783
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
784
|
-
TypedData_Get_Struct(m->regexp, re2_pattern, &re2_regexp_data_type, p);
|
|
1097
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
1098
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
785
1099
|
|
|
786
1100
|
VALUE array = rb_ary_new2(m->number_of_matches - 1);
|
|
787
1101
|
for (int i = 1; i < m->number_of_matches; ++i) {
|
|
788
1102
|
re2::StringPiece *match = &m->matches[i];
|
|
789
1103
|
|
|
790
|
-
if (match->
|
|
1104
|
+
if (match->data() == nullptr) {
|
|
791
1105
|
rb_ary_push(array, Qnil);
|
|
792
1106
|
} else {
|
|
793
1107
|
rb_ary_push(array, encoded_str_new(match->data(), match->size(),
|
|
@@ -813,14 +1127,14 @@ static VALUE re2_matchdata_deconstruct(const VALUE self) {
|
|
|
813
1127
|
* @param [Array<Symbol>, nil] keys an array of `Symbol` capturing group names
|
|
814
1128
|
* or `nil` to return all names
|
|
815
1129
|
* @example
|
|
816
|
-
* m = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').
|
|
1130
|
+
* m = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').partial_match('123 abc')
|
|
817
1131
|
* m.deconstruct_keys(nil) #=> {numbers: "123", letters: "abc"}
|
|
818
1132
|
* m.deconstruct_keys([:numbers]) #=> {numbers: "123"}
|
|
819
1133
|
* m.deconstruct_keys([:fruit]) #=> {}
|
|
820
1134
|
* m.deconstruct_keys([:letters, :fruit]) #=> {letters: "abc"}
|
|
821
1135
|
*
|
|
822
1136
|
* @example pattern matching
|
|
823
|
-
* case RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').
|
|
1137
|
+
* case RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').partial_match('123 abc')
|
|
824
1138
|
* in numbers:, letters:
|
|
825
1139
|
* puts "Numbers: #{numbers}, letters: #{letters}"
|
|
826
1140
|
* else
|
|
@@ -828,20 +1142,17 @@ static VALUE re2_matchdata_deconstruct(const VALUE self) {
|
|
|
828
1142
|
* end
|
|
829
1143
|
*/
|
|
830
1144
|
static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys) {
|
|
831
|
-
re2_matchdata *m;
|
|
832
|
-
re2_pattern *p;
|
|
833
|
-
|
|
834
|
-
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, m);
|
|
835
|
-
TypedData_Get_Struct(m->regexp, re2_pattern, &re2_regexp_data_type, p);
|
|
1145
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
1146
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
836
1147
|
|
|
837
|
-
const
|
|
1148
|
+
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
838
1149
|
VALUE capturing_groups = rb_hash_new();
|
|
839
1150
|
|
|
840
1151
|
if (NIL_P(keys)) {
|
|
841
|
-
for (
|
|
1152
|
+
for (const auto& group : groups) {
|
|
842
1153
|
rb_hash_aset(capturing_groups,
|
|
843
|
-
ID2SYM(
|
|
844
|
-
re2_matchdata_nth_match(
|
|
1154
|
+
ID2SYM(rb_intern2(group.first.data(), group.first.size())),
|
|
1155
|
+
re2_matchdata_nth_match(group.second, self));
|
|
845
1156
|
}
|
|
846
1157
|
} else {
|
|
847
1158
|
Check_Type(keys, T_ARRAY);
|
|
@@ -851,7 +1162,7 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
|
|
|
851
1162
|
VALUE key = rb_ary_entry(keys, i);
|
|
852
1163
|
Check_Type(key, T_SYMBOL);
|
|
853
1164
|
const char *name = rb_id2name(SYM2ID(key));
|
|
854
|
-
|
|
1165
|
+
auto search = groups.find(name);
|
|
855
1166
|
|
|
856
1167
|
if (search != groups.end()) {
|
|
857
1168
|
rb_hash_aset(capturing_groups, key, re2_matchdata_nth_match(search->second, self));
|
|
@@ -865,6 +1176,151 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
|
|
|
865
1176
|
return capturing_groups;
|
|
866
1177
|
}
|
|
867
1178
|
|
|
1179
|
+
/*
|
|
1180
|
+
* Returns a hash of capturing group names to matched strings.
|
|
1181
|
+
*
|
|
1182
|
+
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
1183
|
+
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
1184
|
+
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
1185
|
+
*
|
|
1186
|
+
* @overload named_captures
|
|
1187
|
+
* Returns a hash with string keys.
|
|
1188
|
+
*
|
|
1189
|
+
* @return [Hash] a hash of capturing group names to matching strings
|
|
1190
|
+
* @example
|
|
1191
|
+
* m = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').partial_match('123 abc')
|
|
1192
|
+
* m.named_captures #=> {"numbers" => "123", "letters" => "abc"}
|
|
1193
|
+
*
|
|
1194
|
+
* @overload named_captures(symbolize_names:)
|
|
1195
|
+
* Returns a hash with string or symbol keys.
|
|
1196
|
+
*
|
|
1197
|
+
* @param [Boolean] symbolize_names whether to return group names as symbols
|
|
1198
|
+
* @return [Hash] a hash of capturing group names to matching strings
|
|
1199
|
+
* @example
|
|
1200
|
+
* m = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').partial_match('123 abc')
|
|
1201
|
+
* m.named_captures
|
|
1202
|
+
* #=> {"numbers" => "123", "letters" => "abc"}
|
|
1203
|
+
* m.named_captures(symbolize_names: true) #=> {numbers: "123", letters: "abc"}
|
|
1204
|
+
*/
|
|
1205
|
+
static VALUE re2_matchdata_named_captures(int argc, VALUE *argv, const VALUE self) {
|
|
1206
|
+
VALUE opts;
|
|
1207
|
+
rb_scan_args(argc, argv, "0:", &opts);
|
|
1208
|
+
|
|
1209
|
+
bool symbolize = false;
|
|
1210
|
+
if (!NIL_P(opts)) {
|
|
1211
|
+
VALUE sym = rb_hash_aref(opts, ID2SYM(id_symbolize_names));
|
|
1212
|
+
symbolize = RTEST(sym);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
1216
|
+
re2_pattern *p = unwrap_re2_regexp(m->regexp);
|
|
1217
|
+
|
|
1218
|
+
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
1219
|
+
VALUE result = rb_hash_new();
|
|
1220
|
+
|
|
1221
|
+
for (const auto& group : groups) {
|
|
1222
|
+
VALUE key;
|
|
1223
|
+
if (symbolize) {
|
|
1224
|
+
key = ID2SYM(rb_intern2(group.first.data(), group.first.size()));
|
|
1225
|
+
} else {
|
|
1226
|
+
key = encoded_str_new(group.first.data(), group.first.size(),
|
|
1227
|
+
p->pattern->options().encoding());
|
|
1228
|
+
}
|
|
1229
|
+
rb_hash_aset(result, key, re2_matchdata_nth_match(group.second, self));
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
return result;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
/*
|
|
1236
|
+
* Returns an array of names of named capturing groups. Names are returned in
|
|
1237
|
+
* alphabetical order rather than definition order, as RE2 stores named groups
|
|
1238
|
+
* internally in a sorted map.
|
|
1239
|
+
*
|
|
1240
|
+
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
1241
|
+
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
1242
|
+
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
1243
|
+
*
|
|
1244
|
+
* @return [Array<String>] an array of names of named capturing groups
|
|
1245
|
+
* @example
|
|
1246
|
+
* m = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').partial_match('123 abc')
|
|
1247
|
+
* m.names #=> ["letters", "numbers"]
|
|
1248
|
+
*/
|
|
1249
|
+
static VALUE re2_matchdata_names(const VALUE self) {
|
|
1250
|
+
re2_matchdata *m = unwrap_re2_matchdata(self);
|
|
1251
|
+
|
|
1252
|
+
return re2_regexp_names(m->regexp);
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/*
|
|
1256
|
+
* Returns an array of match values at the given indices or names.
|
|
1257
|
+
*
|
|
1258
|
+
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
1259
|
+
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
1260
|
+
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
1261
|
+
*
|
|
1262
|
+
* @param [Integer, String, Symbol] indexes the indices or names of
|
|
1263
|
+
* the matches to fetch
|
|
1264
|
+
* @return [Array<String, nil>] the values at the given indices or names
|
|
1265
|
+
* @example
|
|
1266
|
+
* m = RE2::Regexp.new('(?P<a>\d+) (?P<b>\d+)').partial_match("123 456")
|
|
1267
|
+
* m.values_at(1, 2) #=> ["123", "456"]
|
|
1268
|
+
* m.values_at(:a, :b) #=> ["123", "456"]
|
|
1269
|
+
* m.values_at(1, :b) #=> ["123", "456"]
|
|
1270
|
+
*/
|
|
1271
|
+
static VALUE re2_matchdata_values_at(int argc, VALUE *argv, const VALUE self) {
|
|
1272
|
+
unwrap_re2_matchdata(self);
|
|
1273
|
+
|
|
1274
|
+
VALUE result = rb_ary_new2(argc);
|
|
1275
|
+
|
|
1276
|
+
for (int i = 0; i < argc; ++i) {
|
|
1277
|
+
VALUE idx = argv[i];
|
|
1278
|
+
|
|
1279
|
+
if (TYPE(idx) == T_STRING) {
|
|
1280
|
+
rb_ary_push(result, re2_matchdata_named_match(
|
|
1281
|
+
std::string(RSTRING_PTR(idx), RSTRING_LEN(idx)), self));
|
|
1282
|
+
} else if (SYMBOL_P(idx)) {
|
|
1283
|
+
rb_ary_push(result, re2_matchdata_named_match(
|
|
1284
|
+
rb_id2name(SYM2ID(idx)), self));
|
|
1285
|
+
} else {
|
|
1286
|
+
rb_ary_push(result, re2_matchdata_nth_match(NUM2INT(idx), self));
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
return result;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
static VALUE re2_matchdata_initialize_copy(VALUE self, VALUE other) {
|
|
1294
|
+
re2_matchdata *self_m;
|
|
1295
|
+
re2_matchdata *other_m = unwrap_re2_matchdata(other);
|
|
1296
|
+
|
|
1297
|
+
TypedData_Get_Struct(self, re2_matchdata, &re2_matchdata_data_type, self_m);
|
|
1298
|
+
|
|
1299
|
+
if (self_m->matches) {
|
|
1300
|
+
delete[] self_m->matches;
|
|
1301
|
+
self_m->matches = nullptr;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
self_m->number_of_matches = other_m->number_of_matches;
|
|
1305
|
+
RB_OBJ_WRITE(self, &self_m->regexp, other_m->regexp);
|
|
1306
|
+
RB_OBJ_WRITE(self, &self_m->text, other_m->text);
|
|
1307
|
+
|
|
1308
|
+
if (other_m->matches) {
|
|
1309
|
+
self_m->matches = new(std::nothrow) re2::StringPiece[other_m->number_of_matches];
|
|
1310
|
+
if (self_m->matches == nullptr) {
|
|
1311
|
+
rb_raise(rb_eNoMemError,
|
|
1312
|
+
"not enough memory to allocate StringPiece for matches");
|
|
1313
|
+
}
|
|
1314
|
+
for (int i = 0; i < other_m->number_of_matches; ++i) {
|
|
1315
|
+
self_m->matches[i] = other_m->matches[i];
|
|
1316
|
+
}
|
|
1317
|
+
} else {
|
|
1318
|
+
self_m->matches = nullptr;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
return self;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
868
1324
|
/*
|
|
869
1325
|
* Shorthand to compile a new {RE2::Regexp}.
|
|
870
1326
|
*
|
|
@@ -920,6 +1376,13 @@ static VALUE re2_regexp_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
920
1376
|
|
|
921
1377
|
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
922
1378
|
|
|
1379
|
+
rb_check_frozen(self);
|
|
1380
|
+
|
|
1381
|
+
if (p->pattern) {
|
|
1382
|
+
delete p->pattern;
|
|
1383
|
+
p->pattern = nullptr;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
923
1386
|
if (RTEST(options)) {
|
|
924
1387
|
RE2::Options re2_options;
|
|
925
1388
|
parse_re2_options(&re2_options, options);
|
|
@@ -931,10 +1394,36 @@ static VALUE re2_regexp_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
931
1394
|
re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)));
|
|
932
1395
|
}
|
|
933
1396
|
|
|
934
|
-
if (p->pattern ==
|
|
1397
|
+
if (p->pattern == nullptr) {
|
|
1398
|
+
rb_raise(rb_eNoMemError, "not enough memory to allocate RE2 object");
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
rb_obj_freeze(self);
|
|
1402
|
+
|
|
1403
|
+
return self;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
static VALUE re2_regexp_initialize_copy(VALUE self, VALUE other) {
|
|
1407
|
+
re2_pattern *self_p;
|
|
1408
|
+
re2_pattern *other_p = unwrap_re2_regexp(other);
|
|
1409
|
+
|
|
1410
|
+
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, self_p);
|
|
1411
|
+
|
|
1412
|
+
rb_check_frozen(self);
|
|
1413
|
+
|
|
1414
|
+
if (self_p->pattern) {
|
|
1415
|
+
delete self_p->pattern;
|
|
1416
|
+
self_p->pattern = nullptr;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
self_p->pattern = new(std::nothrow) RE2(other_p->pattern->pattern(),
|
|
1420
|
+
other_p->pattern->options());
|
|
1421
|
+
if (self_p->pattern == nullptr) {
|
|
935
1422
|
rb_raise(rb_eNoMemError, "not enough memory to allocate RE2 object");
|
|
936
1423
|
}
|
|
937
1424
|
|
|
1425
|
+
rb_obj_freeze(self);
|
|
1426
|
+
|
|
938
1427
|
return self;
|
|
939
1428
|
}
|
|
940
1429
|
|
|
@@ -952,9 +1441,7 @@ static VALUE re2_regexp_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
952
1441
|
* re2.inspect #=> "#<RE2::Regexp /woo?/>"
|
|
953
1442
|
*/
|
|
954
1443
|
static VALUE re2_regexp_inspect(const VALUE self) {
|
|
955
|
-
re2_pattern *p;
|
|
956
|
-
|
|
957
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1444
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
958
1445
|
|
|
959
1446
|
std::ostringstream output;
|
|
960
1447
|
|
|
@@ -977,8 +1464,7 @@ static VALUE re2_regexp_inspect(const VALUE self) {
|
|
|
977
1464
|
* re2.to_s #=> "woo?"
|
|
978
1465
|
*/
|
|
979
1466
|
static VALUE re2_regexp_to_s(const VALUE self) {
|
|
980
|
-
re2_pattern *p;
|
|
981
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1467
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
982
1468
|
|
|
983
1469
|
return encoded_str_new(p->pattern->pattern().data(),
|
|
984
1470
|
p->pattern->pattern().size(),
|
|
@@ -994,8 +1480,7 @@ static VALUE re2_regexp_to_s(const VALUE self) {
|
|
|
994
1480
|
* re2.ok? #=> true
|
|
995
1481
|
*/
|
|
996
1482
|
static VALUE re2_regexp_ok(const VALUE self) {
|
|
997
|
-
re2_pattern *p;
|
|
998
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1483
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
999
1484
|
|
|
1000
1485
|
return BOOL2RUBY(p->pattern->ok());
|
|
1001
1486
|
}
|
|
@@ -1010,8 +1495,7 @@ static VALUE re2_regexp_ok(const VALUE self) {
|
|
|
1010
1495
|
* re2.utf8? #=> true
|
|
1011
1496
|
*/
|
|
1012
1497
|
static VALUE re2_regexp_utf8(const VALUE self) {
|
|
1013
|
-
re2_pattern *p;
|
|
1014
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1498
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1015
1499
|
|
|
1016
1500
|
return BOOL2RUBY(p->pattern->options().encoding() == RE2::Options::EncodingUTF8);
|
|
1017
1501
|
}
|
|
@@ -1026,8 +1510,7 @@ static VALUE re2_regexp_utf8(const VALUE self) {
|
|
|
1026
1510
|
* re2.posix_syntax? #=> true
|
|
1027
1511
|
*/
|
|
1028
1512
|
static VALUE re2_regexp_posix_syntax(const VALUE self) {
|
|
1029
|
-
re2_pattern *p;
|
|
1030
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1513
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1031
1514
|
|
|
1032
1515
|
return BOOL2RUBY(p->pattern->options().posix_syntax());
|
|
1033
1516
|
}
|
|
@@ -1042,8 +1525,7 @@ static VALUE re2_regexp_posix_syntax(const VALUE self) {
|
|
|
1042
1525
|
* re2.longest_match? #=> true
|
|
1043
1526
|
*/
|
|
1044
1527
|
static VALUE re2_regexp_longest_match(const VALUE self) {
|
|
1045
|
-
re2_pattern *p;
|
|
1046
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1528
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1047
1529
|
|
|
1048
1530
|
return BOOL2RUBY(p->pattern->options().longest_match());
|
|
1049
1531
|
}
|
|
@@ -1058,8 +1540,7 @@ static VALUE re2_regexp_longest_match(const VALUE self) {
|
|
|
1058
1540
|
* re2.log_errors? #=> true
|
|
1059
1541
|
*/
|
|
1060
1542
|
static VALUE re2_regexp_log_errors(const VALUE self) {
|
|
1061
|
-
re2_pattern *p;
|
|
1062
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1543
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1063
1544
|
|
|
1064
1545
|
return BOOL2RUBY(p->pattern->options().log_errors());
|
|
1065
1546
|
}
|
|
@@ -1073,8 +1554,7 @@ static VALUE re2_regexp_log_errors(const VALUE self) {
|
|
|
1073
1554
|
* re2.max_mem #=> 1024
|
|
1074
1555
|
*/
|
|
1075
1556
|
static VALUE re2_regexp_max_mem(const VALUE self) {
|
|
1076
|
-
re2_pattern *p;
|
|
1077
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1557
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1078
1558
|
|
|
1079
1559
|
return INT2FIX(p->pattern->options().max_mem());
|
|
1080
1560
|
}
|
|
@@ -1089,8 +1569,7 @@ static VALUE re2_regexp_max_mem(const VALUE self) {
|
|
|
1089
1569
|
* re2.literal? #=> true
|
|
1090
1570
|
*/
|
|
1091
1571
|
static VALUE re2_regexp_literal(const VALUE self) {
|
|
1092
|
-
re2_pattern *p;
|
|
1093
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1572
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1094
1573
|
|
|
1095
1574
|
return BOOL2RUBY(p->pattern->options().literal());
|
|
1096
1575
|
}
|
|
@@ -1105,8 +1584,7 @@ static VALUE re2_regexp_literal(const VALUE self) {
|
|
|
1105
1584
|
* re2.never_nl? #=> true
|
|
1106
1585
|
*/
|
|
1107
1586
|
static VALUE re2_regexp_never_nl(const VALUE self) {
|
|
1108
|
-
re2_pattern *p;
|
|
1109
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1587
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1110
1588
|
|
|
1111
1589
|
return BOOL2RUBY(p->pattern->options().never_nl());
|
|
1112
1590
|
}
|
|
@@ -1121,8 +1599,7 @@ static VALUE re2_regexp_never_nl(const VALUE self) {
|
|
|
1121
1599
|
* re2.case_sensitive? #=> true
|
|
1122
1600
|
*/
|
|
1123
1601
|
static VALUE re2_regexp_case_sensitive(const VALUE self) {
|
|
1124
|
-
re2_pattern *p;
|
|
1125
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1602
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1126
1603
|
|
|
1127
1604
|
return BOOL2RUBY(p->pattern->options().case_sensitive());
|
|
1128
1605
|
}
|
|
@@ -1151,8 +1628,7 @@ static VALUE re2_regexp_case_insensitive(const VALUE self) {
|
|
|
1151
1628
|
* re2.perl_classes? #=> true
|
|
1152
1629
|
*/
|
|
1153
1630
|
static VALUE re2_regexp_perl_classes(const VALUE self) {
|
|
1154
|
-
re2_pattern *p;
|
|
1155
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1631
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1156
1632
|
|
|
1157
1633
|
return BOOL2RUBY(p->pattern->options().perl_classes());
|
|
1158
1634
|
}
|
|
@@ -1167,8 +1643,7 @@ static VALUE re2_regexp_perl_classes(const VALUE self) {
|
|
|
1167
1643
|
* re2.word_boundary? #=> true
|
|
1168
1644
|
*/
|
|
1169
1645
|
static VALUE re2_regexp_word_boundary(const VALUE self) {
|
|
1170
|
-
re2_pattern *p;
|
|
1171
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1646
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1172
1647
|
|
|
1173
1648
|
return BOOL2RUBY(p->pattern->options().word_boundary());
|
|
1174
1649
|
}
|
|
@@ -1183,8 +1658,7 @@ static VALUE re2_regexp_word_boundary(const VALUE self) {
|
|
|
1183
1658
|
* re2.one_line? #=> true
|
|
1184
1659
|
*/
|
|
1185
1660
|
static VALUE re2_regexp_one_line(const VALUE self) {
|
|
1186
|
-
re2_pattern *p;
|
|
1187
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1661
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1188
1662
|
|
|
1189
1663
|
return BOOL2RUBY(p->pattern->options().one_line());
|
|
1190
1664
|
}
|
|
@@ -1196,8 +1670,7 @@ static VALUE re2_regexp_one_line(const VALUE self) {
|
|
|
1196
1670
|
* @return [String, nil] the error string or `nil`
|
|
1197
1671
|
*/
|
|
1198
1672
|
static VALUE re2_regexp_error(const VALUE self) {
|
|
1199
|
-
re2_pattern *p;
|
|
1200
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1673
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1201
1674
|
|
|
1202
1675
|
if (p->pattern->ok()) {
|
|
1203
1676
|
return Qnil;
|
|
@@ -1217,8 +1690,7 @@ static VALUE re2_regexp_error(const VALUE self) {
|
|
|
1217
1690
|
* @return [String, nil] the offending portion of the regexp or `nil`
|
|
1218
1691
|
*/
|
|
1219
1692
|
static VALUE re2_regexp_error_arg(const VALUE self) {
|
|
1220
|
-
re2_pattern *p;
|
|
1221
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1693
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1222
1694
|
|
|
1223
1695
|
if (p->pattern->ok()) {
|
|
1224
1696
|
return Qnil;
|
|
@@ -1237,8 +1709,7 @@ static VALUE re2_regexp_error_arg(const VALUE self) {
|
|
|
1237
1709
|
* @return [Integer] the regexp "cost"
|
|
1238
1710
|
*/
|
|
1239
1711
|
static VALUE re2_regexp_program_size(const VALUE self) {
|
|
1240
|
-
re2_pattern *p;
|
|
1241
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1712
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1242
1713
|
|
|
1243
1714
|
return INT2FIX(p->pattern->ProgramSize());
|
|
1244
1715
|
}
|
|
@@ -1249,9 +1720,7 @@ static VALUE re2_regexp_program_size(const VALUE self) {
|
|
|
1249
1720
|
* @return [Hash] the options
|
|
1250
1721
|
*/
|
|
1251
1722
|
static VALUE re2_regexp_options(const VALUE self) {
|
|
1252
|
-
re2_pattern *p;
|
|
1253
|
-
|
|
1254
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1723
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1255
1724
|
VALUE options = rb_hash_new();
|
|
1256
1725
|
|
|
1257
1726
|
rb_hash_aset(options, ID2SYM(id_utf8),
|
|
@@ -1301,8 +1770,7 @@ static VALUE re2_regexp_options(const VALUE self) {
|
|
|
1301
1770
|
* @return [Integer] the number of capturing subpatterns
|
|
1302
1771
|
*/
|
|
1303
1772
|
static VALUE re2_regexp_number_of_capturing_groups(const VALUE self) {
|
|
1304
|
-
re2_pattern *p;
|
|
1305
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1773
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1306
1774
|
|
|
1307
1775
|
return INT2FIX(p->pattern->NumberOfCapturingGroups());
|
|
1308
1776
|
}
|
|
@@ -1317,17 +1785,15 @@ static VALUE re2_regexp_number_of_capturing_groups(const VALUE self) {
|
|
|
1317
1785
|
* @return [Hash] a hash of names to capturing indices
|
|
1318
1786
|
*/
|
|
1319
1787
|
static VALUE re2_regexp_named_capturing_groups(const VALUE self) {
|
|
1320
|
-
re2_pattern *p;
|
|
1321
|
-
|
|
1322
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1323
|
-
const std::map<std::string, int>& groups = p->pattern->NamedCapturingGroups();
|
|
1788
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1789
|
+
const auto& groups = p->pattern->NamedCapturingGroups();
|
|
1324
1790
|
VALUE capturing_groups = rb_hash_new();
|
|
1325
1791
|
|
|
1326
|
-
for (
|
|
1792
|
+
for (const auto& group : groups) {
|
|
1327
1793
|
rb_hash_aset(capturing_groups,
|
|
1328
|
-
encoded_str_new(
|
|
1794
|
+
encoded_str_new(group.first.data(), group.first.size(),
|
|
1329
1795
|
p->pattern->options().encoding()),
|
|
1330
|
-
INT2FIX(
|
|
1796
|
+
INT2FIX(group.second));
|
|
1331
1797
|
}
|
|
1332
1798
|
|
|
1333
1799
|
return capturing_groups;
|
|
@@ -1422,18 +1888,19 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1422
1888
|
|
|
1423
1889
|
rb_scan_args(argc, argv, "11", &text, &options);
|
|
1424
1890
|
|
|
1425
|
-
/*
|
|
1891
|
+
/* Coerce and freeze text to prevent mutation. */
|
|
1426
1892
|
StringValue(text);
|
|
1893
|
+
text = rb_str_new_frozen(text);
|
|
1427
1894
|
|
|
1428
|
-
|
|
1895
|
+
p = unwrap_re2_regexp(self);
|
|
1429
1896
|
|
|
1430
1897
|
int n;
|
|
1431
|
-
|
|
1432
|
-
|
|
1898
|
+
size_t startpos = 0;
|
|
1899
|
+
size_t endpos = RSTRING_LEN(text);
|
|
1433
1900
|
RE2::Anchor anchor = RE2::UNANCHORED;
|
|
1434
1901
|
|
|
1435
1902
|
if (RTEST(options)) {
|
|
1436
|
-
if (
|
|
1903
|
+
if (RB_INTEGER_TYPE_P(options)) {
|
|
1437
1904
|
n = NUM2INT(options);
|
|
1438
1905
|
|
|
1439
1906
|
if (n < 0) {
|
|
@@ -1447,13 +1914,13 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1447
1914
|
VALUE endpos_option = rb_hash_aref(options, ID2SYM(id_endpos));
|
|
1448
1915
|
if (!NIL_P(endpos_option)) {
|
|
1449
1916
|
#ifdef HAVE_ENDPOS_ARGUMENT
|
|
1450
|
-
|
|
1917
|
+
ssize_t endpos_value = NUM2SSIZET(endpos_option);
|
|
1451
1918
|
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
if (endpos < 0) {
|
|
1919
|
+
if (endpos_value < 0) {
|
|
1455
1920
|
rb_raise(rb_eArgError, "endpos should be >= 0");
|
|
1456
1921
|
}
|
|
1922
|
+
|
|
1923
|
+
endpos = static_cast<size_t>(endpos_value);
|
|
1457
1924
|
#else
|
|
1458
1925
|
rb_raise(re2_eRegexpUnsupportedError, "current version of RE2::Match() does not support endpos argument");
|
|
1459
1926
|
#endif
|
|
@@ -1477,8 +1944,6 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1477
1944
|
|
|
1478
1945
|
VALUE submatches_option = rb_hash_aref(options, ID2SYM(id_submatches));
|
|
1479
1946
|
if (!NIL_P(submatches_option)) {
|
|
1480
|
-
Check_Type(submatches_option, T_FIXNUM);
|
|
1481
|
-
|
|
1482
1947
|
n = NUM2INT(submatches_option);
|
|
1483
1948
|
|
|
1484
1949
|
if (n < 0) {
|
|
@@ -1494,13 +1959,13 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1494
1959
|
|
|
1495
1960
|
VALUE startpos_option = rb_hash_aref(options, ID2SYM(id_startpos));
|
|
1496
1961
|
if (!NIL_P(startpos_option)) {
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
startpos = NUM2INT(startpos_option);
|
|
1962
|
+
ssize_t startpos_value = NUM2SSIZET(startpos_option);
|
|
1500
1963
|
|
|
1501
|
-
if (
|
|
1964
|
+
if (startpos_value < 0) {
|
|
1502
1965
|
rb_raise(rb_eArgError, "startpos should be >= 0");
|
|
1503
1966
|
}
|
|
1967
|
+
|
|
1968
|
+
startpos = static_cast<size_t>(startpos_value);
|
|
1504
1969
|
}
|
|
1505
1970
|
}
|
|
1506
1971
|
} else {
|
|
@@ -1515,49 +1980,50 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1515
1980
|
rb_raise(rb_eArgError, "startpos should be <= endpos");
|
|
1516
1981
|
}
|
|
1517
1982
|
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
#else
|
|
1524
|
-
bool matched = p->pattern->Match(
|
|
1525
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)),
|
|
1526
|
-
startpos, anchor, 0, 0);
|
|
1983
|
+
#ifndef HAVE_ENDPOS_ARGUMENT
|
|
1984
|
+
/* Old RE2's Match() takes int startpos. Reject values that would overflow. */
|
|
1985
|
+
if (startpos > INT_MAX) {
|
|
1986
|
+
rb_raise(rb_eRangeError, "startpos should be <= %d", INT_MAX);
|
|
1987
|
+
}
|
|
1527
1988
|
#endif
|
|
1989
|
+
|
|
1990
|
+
if (n == 0) {
|
|
1991
|
+
bool matched = re2_match_without_gvl(
|
|
1992
|
+
p->pattern, text, startpos, endpos, anchor, 0, 0);
|
|
1993
|
+
RB_GC_GUARD(text);
|
|
1994
|
+
|
|
1528
1995
|
return BOOL2RUBY(matched);
|
|
1529
1996
|
} else {
|
|
1997
|
+
if (n == INT_MAX) {
|
|
1998
|
+
rb_raise(rb_eRangeError, "number of matches should be < %d", INT_MAX);
|
|
1999
|
+
}
|
|
2000
|
+
|
|
1530
2001
|
/* Because match returns the whole match as well. */
|
|
1531
2002
|
n += 1;
|
|
1532
2003
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
m->matches = new(std::nothrow) re2::StringPiece[n];
|
|
1536
|
-
RB_OBJ_WRITE(matchdata, &m->regexp, self);
|
|
1537
|
-
if (!RTEST(rb_obj_frozen_p(text))) {
|
|
1538
|
-
text = rb_str_freeze(rb_str_dup(text));
|
|
1539
|
-
}
|
|
1540
|
-
RB_OBJ_WRITE(matchdata, &m->text, text);
|
|
1541
|
-
|
|
1542
|
-
if (m->matches == 0) {
|
|
2004
|
+
re2::StringPiece *matches = new(std::nothrow) re2::StringPiece[n];
|
|
2005
|
+
if (matches == nullptr) {
|
|
1543
2006
|
rb_raise(rb_eNoMemError,
|
|
1544
2007
|
"not enough memory to allocate StringPieces for matches");
|
|
1545
2008
|
}
|
|
1546
2009
|
|
|
1547
|
-
|
|
2010
|
+
bool matched = re2_match_without_gvl(
|
|
2011
|
+
p->pattern, text, startpos, endpos, anchor, matches, n);
|
|
2012
|
+
RB_GC_GUARD(text);
|
|
1548
2013
|
|
|
1549
|
-
#ifdef HAVE_ENDPOS_ARGUMENT
|
|
1550
|
-
bool matched = p->pattern->Match(
|
|
1551
|
-
re2::StringPiece(RSTRING_PTR(m->text), RSTRING_LEN(m->text)),
|
|
1552
|
-
startpos, endpos, anchor, m->matches, n);
|
|
1553
|
-
#else
|
|
1554
|
-
bool matched = p->pattern->Match(
|
|
1555
|
-
re2::StringPiece(RSTRING_PTR(m->text), RSTRING_LEN(m->text)),
|
|
1556
|
-
startpos, anchor, m->matches, n);
|
|
1557
|
-
#endif
|
|
1558
2014
|
if (matched) {
|
|
2015
|
+
VALUE matchdata = rb_class_new_instance(0, 0, re2_cMatchData);
|
|
2016
|
+
TypedData_Get_Struct(matchdata, re2_matchdata, &re2_matchdata_data_type, m);
|
|
2017
|
+
|
|
2018
|
+
RB_OBJ_WRITE(matchdata, &m->regexp, self);
|
|
2019
|
+
RB_OBJ_WRITE(matchdata, &m->text, text);
|
|
2020
|
+
m->matches = matches;
|
|
2021
|
+
m->number_of_matches = n;
|
|
2022
|
+
|
|
1559
2023
|
return matchdata;
|
|
1560
2024
|
} else {
|
|
2025
|
+
delete[] matches;
|
|
2026
|
+
|
|
1561
2027
|
return Qnil;
|
|
1562
2028
|
}
|
|
1563
2029
|
}
|
|
@@ -1568,19 +2034,20 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
1568
2034
|
* {https://github.com/google/re2/blob/bc0faab533e2b27b85b8ad312abf061e33ed6b5d/re2/re2.h#L413-L427
|
|
1569
2035
|
* `PartialMatch`}.
|
|
1570
2036
|
*
|
|
2037
|
+
* @param [String] text the text to search
|
|
1571
2038
|
* @return [Boolean] whether the match was successful
|
|
1572
2039
|
* @raise [TypeError] if text cannot be coerced to a `String`
|
|
1573
2040
|
*/
|
|
1574
2041
|
static VALUE re2_regexp_match_p(const VALUE self, VALUE text) {
|
|
1575
|
-
re2_pattern *p;
|
|
1576
|
-
|
|
1577
|
-
/* Ensure text is a string. */
|
|
1578
2042
|
StringValue(text);
|
|
2043
|
+
text = rb_str_new_frozen(text);
|
|
1579
2044
|
|
|
1580
|
-
|
|
2045
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
2046
|
+
bool matched = re2_match_without_gvl(
|
|
2047
|
+
p->pattern, text, 0, RSTRING_LEN(text), RE2::UNANCHORED, 0, 0);
|
|
2048
|
+
RB_GC_GUARD(text);
|
|
1581
2049
|
|
|
1582
|
-
return BOOL2RUBY(
|
|
1583
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)), *p->pattern));
|
|
2050
|
+
return BOOL2RUBY(matched);
|
|
1584
2051
|
}
|
|
1585
2052
|
|
|
1586
2053
|
/*
|
|
@@ -1588,19 +2055,20 @@ static VALUE re2_regexp_match_p(const VALUE self, VALUE text) {
|
|
|
1588
2055
|
* {https://github.com/google/re2/blob/bc0faab533e2b27b85b8ad312abf061e33ed6b5d/re2/re2.h#L376-L411
|
|
1589
2056
|
* `FullMatch`}.
|
|
1590
2057
|
*
|
|
2058
|
+
* @param [String] text the text to search
|
|
1591
2059
|
* @return [Boolean] whether the match was successful
|
|
1592
2060
|
* @raise [TypeError] if text cannot be coerced to a `String`
|
|
1593
2061
|
*/
|
|
1594
2062
|
static VALUE re2_regexp_full_match_p(const VALUE self, VALUE text) {
|
|
1595
|
-
re2_pattern *p;
|
|
1596
|
-
|
|
1597
|
-
/* Ensure text is a string. */
|
|
1598
2063
|
StringValue(text);
|
|
2064
|
+
text = rb_str_new_frozen(text);
|
|
1599
2065
|
|
|
1600
|
-
|
|
2066
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
2067
|
+
bool matched = re2_match_without_gvl(
|
|
2068
|
+
p->pattern, text, 0, RSTRING_LEN(text), RE2::ANCHOR_BOTH, 0, 0);
|
|
2069
|
+
RB_GC_GUARD(text);
|
|
1601
2070
|
|
|
1602
|
-
return BOOL2RUBY(
|
|
1603
|
-
re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text)), *p->pattern));
|
|
2071
|
+
return BOOL2RUBY(matched);
|
|
1604
2072
|
}
|
|
1605
2073
|
|
|
1606
2074
|
/*
|
|
@@ -1616,20 +2084,22 @@ static VALUE re2_regexp_full_match_p(const VALUE self, VALUE text) {
|
|
|
1616
2084
|
* #=> #<RE2::Scanner:0x0000000000000001>
|
|
1617
2085
|
*/
|
|
1618
2086
|
static VALUE re2_regexp_scan(const VALUE self, VALUE text) {
|
|
1619
|
-
/* Ensure text is a string. */
|
|
1620
2087
|
StringValue(text);
|
|
2088
|
+
text = rb_str_new_frozen(text);
|
|
1621
2089
|
|
|
1622
|
-
re2_pattern *p;
|
|
2090
|
+
re2_pattern *p = unwrap_re2_regexp(self);
|
|
1623
2091
|
re2_scanner *c;
|
|
1624
|
-
|
|
1625
|
-
TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
|
|
1626
2092
|
VALUE scanner = rb_class_new_instance(0, 0, re2_cScanner);
|
|
1627
2093
|
TypedData_Get_Struct(scanner, re2_scanner, &re2_scanner_data_type, c);
|
|
1628
2094
|
|
|
1629
|
-
c->input = new(std::nothrow) re2::StringPiece(
|
|
1630
|
-
RSTRING_PTR(text), RSTRING_LEN(text));
|
|
1631
2095
|
RB_OBJ_WRITE(scanner, &c->regexp, self);
|
|
1632
2096
|
RB_OBJ_WRITE(scanner, &c->text, text);
|
|
2097
|
+
c->input = new(std::nothrow) re2::StringPiece(
|
|
2098
|
+
RSTRING_PTR(c->text), RSTRING_LEN(c->text));
|
|
2099
|
+
if (c->input == nullptr) {
|
|
2100
|
+
rb_raise(rb_eNoMemError,
|
|
2101
|
+
"not enough memory to allocate StringPiece for input");
|
|
2102
|
+
}
|
|
1633
2103
|
|
|
1634
2104
|
if (p->pattern->ok()) {
|
|
1635
2105
|
c->number_of_capturing_groups = p->pattern->NumberOfCapturingGroups();
|
|
@@ -1678,40 +2148,59 @@ static VALUE re2_regexp_match_has_endpos_argument_p(VALUE) {
|
|
|
1678
2148
|
* @raise [TypeError] if the given rewrite or pattern (if not provided as a
|
|
1679
2149
|
* {RE2::Regexp}) cannot be coerced to `String`s
|
|
1680
2150
|
* @example
|
|
1681
|
-
* RE2.
|
|
2151
|
+
* RE2.replace("hello there", "hello", "howdy") #=> "howdy there"
|
|
1682
2152
|
* re2 = RE2::Regexp.new("hel+o")
|
|
1683
|
-
* RE2.
|
|
2153
|
+
* RE2.replace("hello there", re2, "yo") #=> "yo there"
|
|
1684
2154
|
*/
|
|
1685
|
-
static VALUE
|
|
2155
|
+
static VALUE re2_replace(VALUE, VALUE str, VALUE pattern,
|
|
1686
2156
|
VALUE rewrite) {
|
|
1687
|
-
|
|
1688
|
-
StringValue(rewrite);
|
|
1689
|
-
|
|
1690
|
-
re2_pattern *p;
|
|
2157
|
+
re2_pattern *p = nullptr;
|
|
1691
2158
|
|
|
1692
|
-
/*
|
|
1693
|
-
*
|
|
2159
|
+
/* Coerce and freeze all arguments before any C++ allocations so that any
|
|
2160
|
+
* Ruby exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2161
|
+
* memory, and later coercions cannot mutate earlier strings.
|
|
1694
2162
|
*/
|
|
1695
2163
|
StringValue(str);
|
|
2164
|
+
str = rb_str_new_frozen(str);
|
|
2165
|
+
if (rb_obj_is_kind_of(pattern, re2_cRegexp)) {
|
|
2166
|
+
p = unwrap_re2_regexp(pattern);
|
|
2167
|
+
} else {
|
|
2168
|
+
StringValue(pattern);
|
|
2169
|
+
pattern = rb_str_new_frozen(pattern);
|
|
2170
|
+
}
|
|
2171
|
+
StringValue(rewrite);
|
|
2172
|
+
rewrite = rb_str_new_frozen(rewrite);
|
|
2173
|
+
|
|
2174
|
+
/* Take a copy of str so it can be modified in-place by RE2::Replace. */
|
|
1696
2175
|
std::string str_as_string(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
1697
2176
|
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
2177
|
+
nogvl_replace_arg arg;
|
|
2178
|
+
arg.str = &str_as_string;
|
|
2179
|
+
if (p) {
|
|
2180
|
+
arg.pattern = p->pattern;
|
|
2181
|
+
} else {
|
|
2182
|
+
arg.pattern = nullptr;
|
|
2183
|
+
arg.string_pattern = re2::StringPiece(
|
|
2184
|
+
RSTRING_PTR(pattern), RSTRING_LEN(pattern));
|
|
2185
|
+
}
|
|
2186
|
+
arg.rewrite = re2::StringPiece(
|
|
2187
|
+
RSTRING_PTR(rewrite), RSTRING_LEN(rewrite));
|
|
2188
|
+
|
|
2189
|
+
#ifdef _WIN32
|
|
2190
|
+
nogvl_replace(&arg);
|
|
2191
|
+
#else
|
|
2192
|
+
rb_thread_call_without_gvl(nogvl_replace, &arg, NULL, NULL);
|
|
2193
|
+
#endif
|
|
1703
2194
|
|
|
2195
|
+
RB_GC_GUARD(rewrite);
|
|
2196
|
+
RB_GC_GUARD(pattern);
|
|
2197
|
+
|
|
2198
|
+
if (p) {
|
|
1704
2199
|
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
1705
2200
|
p->pattern->options().encoding());
|
|
1706
2201
|
} else {
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
RE2::Replace(&str_as_string,
|
|
1711
|
-
re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)),
|
|
1712
|
-
re2::StringPiece(RSTRING_PTR(rewrite), RSTRING_LEN(rewrite)));
|
|
1713
|
-
|
|
1714
|
-
return encoded_str_new(str_as_string.data(), str_as_string.size(), RE2::Options::EncodingUTF8);
|
|
2202
|
+
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
2203
|
+
RE2::Options::EncodingUTF8);
|
|
1715
2204
|
}
|
|
1716
2205
|
}
|
|
1717
2206
|
|
|
@@ -1732,38 +2221,136 @@ static VALUE re2_Replace(VALUE, VALUE str, VALUE pattern,
|
|
|
1732
2221
|
* @return [String] the resulting string
|
|
1733
2222
|
* @example
|
|
1734
2223
|
* re2 = RE2::Regexp.new("oo?")
|
|
1735
|
-
* RE2.
|
|
1736
|
-
* RE2.
|
|
2224
|
+
* RE2.global_replace("whoops-doops", re2, "e") #=> "wheps-deps"
|
|
2225
|
+
* RE2.global_replace("hello there", "e", "i") #=> "hillo thiri"
|
|
1737
2226
|
*/
|
|
1738
|
-
static VALUE
|
|
2227
|
+
static VALUE re2_global_replace(VALUE, VALUE str, VALUE pattern,
|
|
1739
2228
|
VALUE rewrite) {
|
|
1740
|
-
|
|
2229
|
+
re2_pattern *p = nullptr;
|
|
2230
|
+
|
|
2231
|
+
/* Coerce and freeze all arguments before any C++ allocations so that any
|
|
2232
|
+
* Ruby exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2233
|
+
* memory, and later coercions cannot mutate earlier strings.
|
|
2234
|
+
*/
|
|
2235
|
+
StringValue(str);
|
|
2236
|
+
str = rb_str_new_frozen(str);
|
|
2237
|
+
if (rb_obj_is_kind_of(pattern, re2_cRegexp)) {
|
|
2238
|
+
p = unwrap_re2_regexp(pattern);
|
|
2239
|
+
} else {
|
|
2240
|
+
StringValue(pattern);
|
|
2241
|
+
pattern = rb_str_new_frozen(pattern);
|
|
2242
|
+
}
|
|
1741
2243
|
StringValue(rewrite);
|
|
2244
|
+
rewrite = rb_str_new_frozen(rewrite);
|
|
1742
2245
|
|
|
1743
2246
|
/* Take a copy of str so it can be modified in-place by
|
|
1744
2247
|
* RE2::GlobalReplace.
|
|
1745
2248
|
*/
|
|
1746
|
-
re2_pattern *p;
|
|
1747
|
-
StringValue(str);
|
|
1748
2249
|
std::string str_as_string(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
1749
2250
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
2251
|
+
nogvl_replace_arg arg;
|
|
2252
|
+
arg.str = &str_as_string;
|
|
2253
|
+
if (p) {
|
|
2254
|
+
arg.pattern = p->pattern;
|
|
2255
|
+
} else {
|
|
2256
|
+
arg.pattern = nullptr;
|
|
2257
|
+
arg.string_pattern = re2::StringPiece(
|
|
2258
|
+
RSTRING_PTR(pattern), RSTRING_LEN(pattern));
|
|
2259
|
+
}
|
|
2260
|
+
arg.rewrite = re2::StringPiece(
|
|
2261
|
+
RSTRING_PTR(rewrite), RSTRING_LEN(rewrite));
|
|
2262
|
+
|
|
2263
|
+
#ifdef _WIN32
|
|
2264
|
+
nogvl_global_replace(&arg);
|
|
2265
|
+
#else
|
|
2266
|
+
rb_thread_call_without_gvl(nogvl_global_replace, &arg, NULL, NULL);
|
|
2267
|
+
#endif
|
|
1755
2268
|
|
|
2269
|
+
RB_GC_GUARD(rewrite);
|
|
2270
|
+
RB_GC_GUARD(pattern);
|
|
2271
|
+
|
|
2272
|
+
if (p) {
|
|
1756
2273
|
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
1757
2274
|
p->pattern->options().encoding());
|
|
1758
2275
|
} else {
|
|
1759
|
-
|
|
2276
|
+
return encoded_str_new(str_as_string.data(), str_as_string.size(),
|
|
2277
|
+
RE2::Options::EncodingUTF8);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
/*
|
|
2282
|
+
* If `pattern` matches `text`, returns a copy of `rewrite` with substitutions
|
|
2283
|
+
* using
|
|
2284
|
+
* {https://github.com/google/re2/blob/bc0faab533e2b27b85b8ad312abf061e33ed6b5d/re2/re2.h#L499-L510
|
|
2285
|
+
* `Extract`}. Non-matching portions of `text` are ignored.
|
|
2286
|
+
*
|
|
2287
|
+
* Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
|
|
2288
|
+
* returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
|
|
2289
|
+
* {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
|
|
2290
|
+
*
|
|
2291
|
+
* @param [String] text the string from which to extract
|
|
2292
|
+
* @param [String, RE2::Regexp] pattern a regexp matching the text
|
|
2293
|
+
* @param [String] rewrite the rewrite string with `\1`-style substitutions
|
|
2294
|
+
* @return [String, nil] the extracted string on a successful match or nil if
|
|
2295
|
+
* there is no match
|
|
2296
|
+
* @raise [TypeError] if the given rewrite or pattern (if not provided as a
|
|
2297
|
+
* {RE2::Regexp}) cannot be coerced to `String`s
|
|
2298
|
+
* @example
|
|
2299
|
+
* RE2.extract("alice@example.com", '(\w+)@(\w+)', '\2-\1')
|
|
2300
|
+
* #=> "example-alice"
|
|
2301
|
+
* RE2.extract("no match", '(\d+)', '\1') #=> nil
|
|
2302
|
+
*/
|
|
2303
|
+
static VALUE re2_extract(VALUE, VALUE text, VALUE pattern,
|
|
2304
|
+
VALUE rewrite) {
|
|
2305
|
+
re2_pattern *p = nullptr;
|
|
2306
|
+
|
|
2307
|
+
/* Coerce and freeze all arguments before any C++ allocations so that any
|
|
2308
|
+
* Ruby exceptions (via longjmp) cannot bypass C++ destructors and leak
|
|
2309
|
+
* memory, and later coercions cannot mutate earlier strings.
|
|
2310
|
+
*/
|
|
2311
|
+
StringValue(text);
|
|
2312
|
+
text = rb_str_new_frozen(text);
|
|
2313
|
+
if (rb_obj_is_kind_of(pattern, re2_cRegexp)) {
|
|
2314
|
+
p = unwrap_re2_regexp(pattern);
|
|
2315
|
+
} else {
|
|
1760
2316
|
StringValue(pattern);
|
|
2317
|
+
pattern = rb_str_new_frozen(pattern);
|
|
2318
|
+
}
|
|
2319
|
+
StringValue(rewrite);
|
|
2320
|
+
rewrite = rb_str_new_frozen(rewrite);
|
|
1761
2321
|
|
|
1762
|
-
|
|
1763
|
-
re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)),
|
|
1764
|
-
re2::StringPiece(RSTRING_PTR(rewrite), RSTRING_LEN(rewrite)));
|
|
2322
|
+
std::string out;
|
|
1765
2323
|
|
|
1766
|
-
|
|
2324
|
+
nogvl_extract_arg arg;
|
|
2325
|
+
arg.text = re2::StringPiece(RSTRING_PTR(text), RSTRING_LEN(text));
|
|
2326
|
+
if (p) {
|
|
2327
|
+
arg.pattern = p->pattern;
|
|
2328
|
+
} else {
|
|
2329
|
+
arg.pattern = nullptr;
|
|
2330
|
+
arg.string_pattern = re2::StringPiece(
|
|
2331
|
+
RSTRING_PTR(pattern), RSTRING_LEN(pattern));
|
|
2332
|
+
}
|
|
2333
|
+
arg.rewrite = re2::StringPiece(
|
|
2334
|
+
RSTRING_PTR(rewrite), RSTRING_LEN(rewrite));
|
|
2335
|
+
arg.out = &out;
|
|
2336
|
+
arg.extracted = false;
|
|
2337
|
+
|
|
2338
|
+
#ifdef _WIN32
|
|
2339
|
+
nogvl_extract(&arg);
|
|
2340
|
+
#else
|
|
2341
|
+
rb_thread_call_without_gvl(nogvl_extract, &arg, NULL, NULL);
|
|
2342
|
+
#endif
|
|
2343
|
+
|
|
2344
|
+
RB_GC_GUARD(text);
|
|
2345
|
+
RB_GC_GUARD(rewrite);
|
|
2346
|
+
RB_GC_GUARD(pattern);
|
|
2347
|
+
|
|
2348
|
+
if (arg.extracted) {
|
|
2349
|
+
return encoded_str_new(out.data(), out.size(),
|
|
2350
|
+
p ? p->pattern->options().encoding()
|
|
2351
|
+
: RE2::Options::EncodingUTF8);
|
|
2352
|
+
} else {
|
|
2353
|
+
return Qnil;
|
|
1767
2354
|
}
|
|
1768
2355
|
}
|
|
1769
2356
|
|
|
@@ -1778,9 +2365,12 @@ static VALUE re2_GlobalReplace(VALUE, VALUE str, VALUE pattern,
|
|
|
1778
2365
|
* @raise [TypeError] if the given unquoted string cannot be coerced to a `String`
|
|
1779
2366
|
* @return [String] the escaped string
|
|
1780
2367
|
* @example
|
|
1781
|
-
* RE2
|
|
2368
|
+
* RE2.escape("1.5-2.0?") #=> "1\\.5\\-2\\.0\\?"
|
|
2369
|
+
* RE2.quote("1.5-2.0?") #=> "1\\.5\\-2\\.0\\?"
|
|
2370
|
+
* RE2::Regexp.escape("1.5-2.0?") #=> "1\\.5\\-2\\.0\\?"
|
|
2371
|
+
* RE2::Regexp.quote("1.5-2.0?") #=> "1\\.5\\-2\\.0\\?"
|
|
1782
2372
|
*/
|
|
1783
|
-
static VALUE
|
|
2373
|
+
static VALUE re2_escape(VALUE, VALUE unquoted) {
|
|
1784
2374
|
StringValue(unquoted);
|
|
1785
2375
|
|
|
1786
2376
|
std::string quoted_string = RE2::QuoteMeta(
|
|
@@ -1790,7 +2380,7 @@ static VALUE re2_QuoteMeta(VALUE, VALUE unquoted) {
|
|
|
1790
2380
|
}
|
|
1791
2381
|
|
|
1792
2382
|
static void re2_set_free(void *ptr) {
|
|
1793
|
-
re2_set *s =
|
|
2383
|
+
re2_set *s = static_cast<re2_set *>(ptr);
|
|
1794
2384
|
if (s->set) {
|
|
1795
2385
|
delete s->set;
|
|
1796
2386
|
}
|
|
@@ -1798,7 +2388,7 @@ static void re2_set_free(void *ptr) {
|
|
|
1798
2388
|
}
|
|
1799
2389
|
|
|
1800
2390
|
static size_t re2_set_memsize(const void *ptr) {
|
|
1801
|
-
const re2_set *s =
|
|
2391
|
+
const re2_set *s = static_cast<const re2_set *>(ptr);
|
|
1802
2392
|
size_t size = sizeof(*s);
|
|
1803
2393
|
if (s->set) {
|
|
1804
2394
|
size += sizeof(*s->set);
|
|
@@ -1818,9 +2408,18 @@ static const rb_data_type_t re2_set_data_type = {
|
|
|
1818
2408
|
0,
|
|
1819
2409
|
// IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
|
|
1820
2410
|
// macro to update VALUE references, as to trigger write barriers.
|
|
1821
|
-
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
|
|
2411
|
+
RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE
|
|
1822
2412
|
};
|
|
1823
2413
|
|
|
2414
|
+
static re2_set *unwrap_re2_set(VALUE self) {
|
|
2415
|
+
re2_set *s;
|
|
2416
|
+
TypedData_Get_Struct(self, re2_set, &re2_set_data_type, s);
|
|
2417
|
+
if (!s->set) {
|
|
2418
|
+
rb_raise(rb_eTypeError, "uninitialized RE2::Set");
|
|
2419
|
+
}
|
|
2420
|
+
return s;
|
|
2421
|
+
}
|
|
2422
|
+
|
|
1824
2423
|
static VALUE re2_set_allocate(VALUE klass) {
|
|
1825
2424
|
re2_set *s;
|
|
1826
2425
|
VALUE result = TypedData_Make_Struct(klass, re2_set, &re2_set_data_type, s);
|
|
@@ -1828,6 +2427,10 @@ static VALUE re2_set_allocate(VALUE klass) {
|
|
|
1828
2427
|
return result;
|
|
1829
2428
|
}
|
|
1830
2429
|
|
|
2430
|
+
static VALUE re2_set_initialize_copy(VALUE, VALUE) {
|
|
2431
|
+
rb_raise(rb_eTypeError, "cannot copy RE2::Set");
|
|
2432
|
+
}
|
|
2433
|
+
|
|
1831
2434
|
/*
|
|
1832
2435
|
* Returns a new {RE2::Set} object, a collection of patterns that can be
|
|
1833
2436
|
* searched for simultaneously.
|
|
@@ -1898,8 +2501,15 @@ static VALUE re2_set_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
1898
2501
|
parse_re2_options(&re2_options, options);
|
|
1899
2502
|
}
|
|
1900
2503
|
|
|
2504
|
+
rb_check_frozen(self);
|
|
2505
|
+
|
|
2506
|
+
if (s->set) {
|
|
2507
|
+
delete s->set;
|
|
2508
|
+
s->set = nullptr;
|
|
2509
|
+
}
|
|
2510
|
+
|
|
1901
2511
|
s->set = new(std::nothrow) RE2::Set(re2_options, re2_anchor);
|
|
1902
|
-
if (s->set ==
|
|
2512
|
+
if (s->set == nullptr) {
|
|
1903
2513
|
rb_raise(rb_eNoMemError, "not enough memory to allocate RE2::Set object");
|
|
1904
2514
|
}
|
|
1905
2515
|
|
|
@@ -1922,24 +2532,22 @@ static VALUE re2_set_initialize(int argc, VALUE *argv, VALUE self) {
|
|
|
1922
2532
|
static VALUE re2_set_add(VALUE self, VALUE pattern) {
|
|
1923
2533
|
StringValue(pattern);
|
|
1924
2534
|
|
|
1925
|
-
re2_set *s;
|
|
1926
|
-
|
|
2535
|
+
re2_set *s = unwrap_re2_set(self);
|
|
2536
|
+
rb_check_frozen(self);
|
|
1927
2537
|
|
|
1928
|
-
/* To prevent the memory of the err string leaking when we call rb_raise,
|
|
1929
|
-
* take a copy of it and let it go out of scope.
|
|
1930
|
-
*/
|
|
1931
|
-
char msg[100];
|
|
1932
2538
|
int index;
|
|
2539
|
+
VALUE msg;
|
|
1933
2540
|
|
|
1934
2541
|
{
|
|
1935
2542
|
std::string err;
|
|
1936
2543
|
index = s->set->Add(
|
|
1937
2544
|
re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)), &err);
|
|
1938
|
-
|
|
2545
|
+
msg = rb_str_new(err.data(), err.size());
|
|
1939
2546
|
}
|
|
1940
2547
|
|
|
1941
2548
|
if (index < 0) {
|
|
1942
|
-
rb_raise(rb_eArgError,
|
|
2549
|
+
rb_raise(rb_eArgError,
|
|
2550
|
+
"str rejected by RE2::Set->Add(): %s", RSTRING_PTR(msg));
|
|
1943
2551
|
}
|
|
1944
2552
|
|
|
1945
2553
|
return INT2FIX(index);
|
|
@@ -1956,10 +2564,35 @@ static VALUE re2_set_add(VALUE self, VALUE pattern) {
|
|
|
1956
2564
|
* set.compile #=> true
|
|
1957
2565
|
*/
|
|
1958
2566
|
static VALUE re2_set_compile(VALUE self) {
|
|
1959
|
-
re2_set *s;
|
|
1960
|
-
|
|
2567
|
+
re2_set *s = unwrap_re2_set(self);
|
|
2568
|
+
rb_check_frozen(self);
|
|
1961
2569
|
|
|
1962
|
-
|
|
2570
|
+
bool compiled = s->set->Compile();
|
|
2571
|
+
|
|
2572
|
+
if (compiled) {
|
|
2573
|
+
rb_obj_freeze(self);
|
|
2574
|
+
}
|
|
2575
|
+
|
|
2576
|
+
return BOOL2RUBY(compiled);
|
|
2577
|
+
}
|
|
2578
|
+
|
|
2579
|
+
/*
|
|
2580
|
+
* Returns the size of the {RE2::Set}.
|
|
2581
|
+
*
|
|
2582
|
+
* @return [Integer] the number of patterns in the set
|
|
2583
|
+
* @example
|
|
2584
|
+
* set = RE2::Set.new
|
|
2585
|
+
* set.add("abc")
|
|
2586
|
+
* set.size #=> 1
|
|
2587
|
+
*/
|
|
2588
|
+
static VALUE re2_set_size(VALUE self) {
|
|
2589
|
+
#ifdef HAVE_SET_SIZE
|
|
2590
|
+
re2_set *s = unwrap_re2_set(self);
|
|
2591
|
+
|
|
2592
|
+
return INT2FIX(s->set->Size());
|
|
2593
|
+
#else
|
|
2594
|
+
rb_raise(re2_eSetUnsupportedError, "current version of RE2::Set does not have Size method");
|
|
2595
|
+
#endif
|
|
1963
2596
|
}
|
|
1964
2597
|
|
|
1965
2598
|
/*
|
|
@@ -1978,6 +2611,19 @@ static VALUE re2_set_match_raises_errors_p(VALUE) {
|
|
|
1978
2611
|
#endif
|
|
1979
2612
|
}
|
|
1980
2613
|
|
|
2614
|
+
/*
|
|
2615
|
+
* Returns whether the underlying RE2 version has a Set::Size method.
|
|
2616
|
+
*
|
|
2617
|
+
* @return [Boolean] whether the underlying RE2 has a Set::Size method
|
|
2618
|
+
*/
|
|
2619
|
+
static VALUE re2_set_size_p(VALUE) {
|
|
2620
|
+
#ifdef HAVE_SET_SIZE
|
|
2621
|
+
return Qtrue;
|
|
2622
|
+
#else
|
|
2623
|
+
return Qfalse;
|
|
2624
|
+
#endif
|
|
2625
|
+
}
|
|
2626
|
+
|
|
1981
2627
|
/*
|
|
1982
2628
|
* Matches the given text against patterns in the set, returning an array of
|
|
1983
2629
|
* integer indices of the matching patterns if matched or an empty array if
|
|
@@ -2024,8 +2670,9 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2024
2670
|
rb_scan_args(argc, argv, "11", &str, &options);
|
|
2025
2671
|
|
|
2026
2672
|
StringValue(str);
|
|
2027
|
-
|
|
2028
|
-
|
|
2673
|
+
str = rb_str_new_frozen(str);
|
|
2674
|
+
|
|
2675
|
+
re2_set *s = unwrap_re2_set(self);
|
|
2029
2676
|
|
|
2030
2677
|
if (RTEST(options)) {
|
|
2031
2678
|
Check_Type(options, T_HASH);
|
|
@@ -2041,8 +2688,21 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2041
2688
|
if (raise_exception) {
|
|
2042
2689
|
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
2043
2690
|
RE2::Set::ErrorInfo e;
|
|
2044
|
-
|
|
2045
|
-
|
|
2691
|
+
nogvl_set_match_arg arg;
|
|
2692
|
+
arg.set = s->set;
|
|
2693
|
+
arg.text = re2::StringPiece(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
2694
|
+
arg.v = &v;
|
|
2695
|
+
arg.error_info = &e;
|
|
2696
|
+
arg.matched = false;
|
|
2697
|
+
|
|
2698
|
+
#ifdef _WIN32
|
|
2699
|
+
nogvl_set_match(&arg);
|
|
2700
|
+
#else
|
|
2701
|
+
rb_thread_call_without_gvl(nogvl_set_match, &arg, NULL, NULL);
|
|
2702
|
+
#endif
|
|
2703
|
+
RB_GC_GUARD(str);
|
|
2704
|
+
|
|
2705
|
+
bool match_failed = !arg.matched;
|
|
2046
2706
|
VALUE result = rb_ary_new2(v.size());
|
|
2047
2707
|
|
|
2048
2708
|
if (match_failed) {
|
|
@@ -2059,8 +2719,8 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2059
2719
|
rb_raise(re2_eSetMatchError, "Unknown RE2::Set::ErrorKind: %d", e.kind);
|
|
2060
2720
|
}
|
|
2061
2721
|
} else {
|
|
2062
|
-
for (
|
|
2063
|
-
rb_ary_push(result, INT2FIX(
|
|
2722
|
+
for (int index : v) {
|
|
2723
|
+
rb_ary_push(result, INT2FIX(index));
|
|
2064
2724
|
}
|
|
2065
2725
|
}
|
|
2066
2726
|
|
|
@@ -2069,13 +2729,27 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2069
2729
|
rb_raise(re2_eSetUnsupportedError, "current version of RE2::Set::Match() does not output error information, :exception option can only be set to false");
|
|
2070
2730
|
#endif
|
|
2071
2731
|
} else {
|
|
2072
|
-
|
|
2073
|
-
|
|
2732
|
+
nogvl_set_match_arg arg;
|
|
2733
|
+
arg.set = s->set;
|
|
2734
|
+
arg.text = re2::StringPiece(RSTRING_PTR(str), RSTRING_LEN(str));
|
|
2735
|
+
arg.v = &v;
|
|
2736
|
+
#ifdef HAVE_ERROR_INFO_ARGUMENT
|
|
2737
|
+
arg.error_info = nullptr;
|
|
2738
|
+
#endif
|
|
2739
|
+
arg.matched = false;
|
|
2740
|
+
|
|
2741
|
+
#ifdef _WIN32
|
|
2742
|
+
nogvl_set_match(&arg);
|
|
2743
|
+
#else
|
|
2744
|
+
rb_thread_call_without_gvl(nogvl_set_match, &arg, NULL, NULL);
|
|
2745
|
+
#endif
|
|
2746
|
+
RB_GC_GUARD(str);
|
|
2747
|
+
|
|
2074
2748
|
VALUE result = rb_ary_new2(v.size());
|
|
2075
2749
|
|
|
2076
|
-
if (matched) {
|
|
2077
|
-
for (
|
|
2078
|
-
rb_ary_push(result, INT2FIX(
|
|
2750
|
+
if (arg.matched) {
|
|
2751
|
+
for (int index : v) {
|
|
2752
|
+
rb_ary_push(result, INT2FIX(index));
|
|
2079
2753
|
}
|
|
2080
2754
|
}
|
|
2081
2755
|
|
|
@@ -2084,6 +2758,8 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
|
|
|
2084
2758
|
}
|
|
2085
2759
|
|
|
2086
2760
|
extern "C" void Init_re2(void) {
|
|
2761
|
+
rb_ext_ractor_safe(true);
|
|
2762
|
+
|
|
2087
2763
|
re2_mRE2 = rb_define_module("RE2");
|
|
2088
2764
|
re2_cRegexp = rb_define_class_under(re2_mRE2, "Regexp", rb_cObject);
|
|
2089
2765
|
re2_eRegexpUnsupportedError = rb_define_class_under(re2_cRegexp,
|
|
@@ -2119,6 +2795,14 @@ extern "C" void Init_re2(void) {
|
|
|
2119
2795
|
RUBY_METHOD_FUNC(re2_matchdata_begin), 1);
|
|
2120
2796
|
rb_define_method(re2_cMatchData, "end",
|
|
2121
2797
|
RUBY_METHOD_FUNC(re2_matchdata_end), 1);
|
|
2798
|
+
rb_define_method(re2_cMatchData, "pre_match",
|
|
2799
|
+
RUBY_METHOD_FUNC(re2_matchdata_pre_match), 0);
|
|
2800
|
+
rb_define_method(re2_cMatchData, "post_match",
|
|
2801
|
+
RUBY_METHOD_FUNC(re2_matchdata_post_match), 0);
|
|
2802
|
+
rb_define_method(re2_cMatchData, "offset",
|
|
2803
|
+
RUBY_METHOD_FUNC(re2_matchdata_offset), 1);
|
|
2804
|
+
rb_define_method(re2_cMatchData, "match_length",
|
|
2805
|
+
RUBY_METHOD_FUNC(re2_matchdata_match_length), 1);
|
|
2122
2806
|
rb_define_method(re2_cMatchData, "[]", RUBY_METHOD_FUNC(re2_matchdata_aref),
|
|
2123
2807
|
-1);
|
|
2124
2808
|
rb_define_method(re2_cMatchData, "to_s",
|
|
@@ -2127,8 +2811,18 @@ extern "C" void Init_re2(void) {
|
|
|
2127
2811
|
RUBY_METHOD_FUNC(re2_matchdata_inspect), 0);
|
|
2128
2812
|
rb_define_method(re2_cMatchData, "deconstruct",
|
|
2129
2813
|
RUBY_METHOD_FUNC(re2_matchdata_deconstruct), 0);
|
|
2814
|
+
rb_define_method(re2_cMatchData, "captures",
|
|
2815
|
+
RUBY_METHOD_FUNC(re2_matchdata_deconstruct), 0);
|
|
2816
|
+
rb_define_method(re2_cMatchData, "named_captures",
|
|
2817
|
+
RUBY_METHOD_FUNC(re2_matchdata_named_captures), -1);
|
|
2818
|
+
rb_define_method(re2_cMatchData, "names",
|
|
2819
|
+
RUBY_METHOD_FUNC(re2_matchdata_names), 0);
|
|
2820
|
+
rb_define_method(re2_cMatchData, "values_at",
|
|
2821
|
+
RUBY_METHOD_FUNC(re2_matchdata_values_at), -1);
|
|
2130
2822
|
rb_define_method(re2_cMatchData, "deconstruct_keys",
|
|
2131
2823
|
RUBY_METHOD_FUNC(re2_matchdata_deconstruct_keys), 1);
|
|
2824
|
+
rb_define_method(re2_cMatchData, "initialize_copy",
|
|
2825
|
+
RUBY_METHOD_FUNC(re2_matchdata_initialize_copy), 1);
|
|
2132
2826
|
|
|
2133
2827
|
rb_define_method(re2_cScanner, "string",
|
|
2134
2828
|
RUBY_METHOD_FUNC(re2_scanner_string), 0);
|
|
@@ -2140,11 +2834,15 @@ extern "C" void Init_re2(void) {
|
|
|
2140
2834
|
RUBY_METHOD_FUNC(re2_scanner_scan), 0);
|
|
2141
2835
|
rb_define_method(re2_cScanner, "rewind",
|
|
2142
2836
|
RUBY_METHOD_FUNC(re2_scanner_rewind), 0);
|
|
2837
|
+
rb_define_method(re2_cScanner, "initialize_copy",
|
|
2838
|
+
RUBY_METHOD_FUNC(re2_scanner_initialize_copy), 1);
|
|
2143
2839
|
|
|
2144
2840
|
rb_define_singleton_method(re2_cRegexp, "match_has_endpos_argument?",
|
|
2145
2841
|
RUBY_METHOD_FUNC(re2_regexp_match_has_endpos_argument_p), 0);
|
|
2146
2842
|
rb_define_method(re2_cRegexp, "initialize",
|
|
2147
2843
|
RUBY_METHOD_FUNC(re2_regexp_initialize), -1);
|
|
2844
|
+
rb_define_method(re2_cRegexp, "initialize_copy",
|
|
2845
|
+
RUBY_METHOD_FUNC(re2_regexp_initialize_copy), 1);
|
|
2148
2846
|
rb_define_method(re2_cRegexp, "ok?", RUBY_METHOD_FUNC(re2_regexp_ok), 0);
|
|
2149
2847
|
rb_define_method(re2_cRegexp, "error", RUBY_METHOD_FUNC(re2_regexp_error),
|
|
2150
2848
|
0);
|
|
@@ -2158,6 +2856,10 @@ extern "C" void Init_re2(void) {
|
|
|
2158
2856
|
RUBY_METHOD_FUNC(re2_regexp_number_of_capturing_groups), 0);
|
|
2159
2857
|
rb_define_method(re2_cRegexp, "named_capturing_groups",
|
|
2160
2858
|
RUBY_METHOD_FUNC(re2_regexp_named_capturing_groups), 0);
|
|
2859
|
+
rb_define_method(re2_cRegexp, "named_captures",
|
|
2860
|
+
RUBY_METHOD_FUNC(re2_regexp_named_capturing_groups), 0);
|
|
2861
|
+
rb_define_method(re2_cRegexp, "names",
|
|
2862
|
+
RUBY_METHOD_FUNC(re2_regexp_names), 0);
|
|
2161
2863
|
rb_define_method(re2_cRegexp, "match", RUBY_METHOD_FUNC(re2_regexp_match),
|
|
2162
2864
|
-1);
|
|
2163
2865
|
rb_define_method(re2_cRegexp, "match?", RUBY_METHOD_FUNC(re2_regexp_match_p),
|
|
@@ -2208,22 +2910,38 @@ extern "C" void Init_re2(void) {
|
|
|
2208
2910
|
|
|
2209
2911
|
rb_define_singleton_method(re2_cSet, "match_raises_errors?",
|
|
2210
2912
|
RUBY_METHOD_FUNC(re2_set_match_raises_errors_p), 0);
|
|
2913
|
+
rb_define_singleton_method(re2_cSet, "size?",
|
|
2914
|
+
RUBY_METHOD_FUNC(re2_set_size_p), 0);
|
|
2211
2915
|
rb_define_method(re2_cSet, "initialize",
|
|
2212
2916
|
RUBY_METHOD_FUNC(re2_set_initialize), -1);
|
|
2917
|
+
rb_define_method(re2_cSet, "initialize_copy",
|
|
2918
|
+
RUBY_METHOD_FUNC(re2_set_initialize_copy), 1);
|
|
2213
2919
|
rb_define_method(re2_cSet, "add", RUBY_METHOD_FUNC(re2_set_add), 1);
|
|
2214
2920
|
rb_define_method(re2_cSet, "compile", RUBY_METHOD_FUNC(re2_set_compile), 0);
|
|
2215
2921
|
rb_define_method(re2_cSet, "match", RUBY_METHOD_FUNC(re2_set_match), -1);
|
|
2922
|
+
rb_define_method(re2_cSet, "size", RUBY_METHOD_FUNC(re2_set_size), 0);
|
|
2923
|
+
rb_define_method(re2_cSet, "length", RUBY_METHOD_FUNC(re2_set_size), 0);
|
|
2216
2924
|
|
|
2925
|
+
rb_define_module_function(re2_mRE2, "replace",
|
|
2926
|
+
RUBY_METHOD_FUNC(re2_replace), 3);
|
|
2217
2927
|
rb_define_module_function(re2_mRE2, "Replace",
|
|
2218
|
-
RUBY_METHOD_FUNC(
|
|
2928
|
+
RUBY_METHOD_FUNC(re2_replace), 3);
|
|
2929
|
+
rb_define_module_function(re2_mRE2, "global_replace",
|
|
2930
|
+
RUBY_METHOD_FUNC(re2_global_replace), 3);
|
|
2219
2931
|
rb_define_module_function(re2_mRE2, "GlobalReplace",
|
|
2220
|
-
RUBY_METHOD_FUNC(
|
|
2932
|
+
RUBY_METHOD_FUNC(re2_global_replace), 3);
|
|
2933
|
+
rb_define_module_function(re2_mRE2, "extract",
|
|
2934
|
+
RUBY_METHOD_FUNC(re2_extract), 3);
|
|
2221
2935
|
rb_define_module_function(re2_mRE2, "QuoteMeta",
|
|
2222
|
-
RUBY_METHOD_FUNC(
|
|
2936
|
+
RUBY_METHOD_FUNC(re2_escape), 1);
|
|
2937
|
+
rb_define_module_function(re2_mRE2, "escape",
|
|
2938
|
+
RUBY_METHOD_FUNC(re2_escape), 1);
|
|
2939
|
+
rb_define_module_function(re2_mRE2, "quote",
|
|
2940
|
+
RUBY_METHOD_FUNC(re2_escape), 1);
|
|
2223
2941
|
rb_define_singleton_method(re2_cRegexp, "escape",
|
|
2224
|
-
RUBY_METHOD_FUNC(
|
|
2942
|
+
RUBY_METHOD_FUNC(re2_escape), 1);
|
|
2225
2943
|
rb_define_singleton_method(re2_cRegexp, "quote",
|
|
2226
|
-
RUBY_METHOD_FUNC(
|
|
2944
|
+
RUBY_METHOD_FUNC(re2_escape), 1);
|
|
2227
2945
|
|
|
2228
2946
|
// (see RE2::Regexp#initialize)
|
|
2229
2947
|
rb_define_singleton_method(re2_cRegexp, "compile",
|
|
@@ -2251,4 +2969,5 @@ extern "C" void Init_re2(void) {
|
|
|
2251
2969
|
id_submatches = rb_intern("submatches");
|
|
2252
2970
|
id_startpos = rb_intern("startpos");
|
|
2253
2971
|
id_endpos = rb_intern("endpos");
|
|
2972
|
+
id_symbolize_names = rb_intern("symbolize_names");
|
|
2254
2973
|
}
|