re2 2.27.0-aarch64-linux-gnu → 2.29.0-aarch64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 226337419b8f43fac84340c4451ffe36ee2adef3ef432f818e9554acde5a4018
4
- data.tar.gz: 6e0e27ac3810ec3c8c93965b371df81565946562143ed3623a49e049825bfe9c
3
+ metadata.gz: f1125158fafd16154fa094bd9fd9ac6933a9d0517d8a0d844db645667a543072
4
+ data.tar.gz: dd096ad81b773eb87c568f7647935f3be79964225edb3093949af3164b10a5d4
5
5
  SHA512:
6
- metadata.gz: 344aebf8c8657553445a58cfea91715cdfeff337c44056a9366ad5f4ff8732c5ea8461d848210cfd35b469ef6f2bb41460766594ac819db4bedf0695c7fc209c
7
- data.tar.gz: fd80d1dda0333874bee69fdbae0876470bc51cbd243a6086d47aa10d74a18753732b6e7d70a29fa7b9d865c6a23a1a89370158dc8e7c0e458d1db45b9d1f2cf0
6
+ metadata.gz: 39473b83200240c8654f3b6c5dff051c9a8a99797fd1fe948019ce6117b4204edd1f05e36cf2702f46f4e2a90957f9fcf1bca113896909eb6e485a36b49ab8af
7
+ data.tar.gz: 11b17f1e714db91c173a2196bea55de38a4db02d79d80ff3e8c12853e866a0290fa48c95a845e08025c60d841ba1a3417e3c2b9ec38e12c10fd68105824a1f6c
data/Gemfile CHANGED
@@ -9,3 +9,8 @@ gem "rake", "> 12.3.2"
9
9
  group :memcheck, optional: true do
10
10
  gem "ruby_memcheck"
11
11
  end
12
+
13
+ group :docs, optional: true do
14
+ gem "redcarpet"
15
+ gem "yard"
16
+ end
data/README.md CHANGED
@@ -6,7 +6,7 @@ Python".
6
6
 
7
7
  [![Build Status](https://github.com/mudge/re2/actions/workflows/tests.yml/badge.svg?branch=main)](https://github.com/mudge/re2/actions)
8
8
 
9
- **Current version:** 2.27.0
9
+ **Current version:** 2.29.0
10
10
  **Bundled RE2 version:** libre2.11 (2025-11-05)
11
11
 
12
12
  ```ruby
@@ -232,6 +232,14 @@ m.offset(1) #=> [4, 7]
232
232
  m.match_length(1) #=> 3
233
233
  ```
234
234
 
235
+ As RE2 works in bytes, the [`byteoffset`](https://mudge.name/re2/RE2/MatchData.html#byteoffset-instance_method), [`bytebegin`](https://mudge.name/re2/RE2/MatchData.html#bytebegin-instance_method) and [`byteend`](https://mudge.name/re2/RE2/MatchData.html#byteend-instance_method) of a match are cheaper to retrieve than their character equivalents:
236
+
237
+ ```ruby
238
+ m = RE2::Regexp.new('(Ruby)').partial_match("I ♥ Ruby")
239
+ m.offset(1) #=> [4, 8]
240
+ m.byteoffset(1) #=> [6, 10]
241
+ ```
242
+
235
243
  `RE2::MatchData` objects can also be used with Ruby's [pattern matching](https://docs.ruby-lang.org/en/3.2/syntax/pattern_matching_rdoc.html):
236
244
 
237
245
  ```ruby
@@ -483,6 +491,8 @@ Alternatively, you can set the `RE2_USE_SYSTEM_LIBRARIES` environment variable i
483
491
  when passing strings with null bytes.
484
492
  * Thanks to [Maciej Gajewski](https://github.com/konieczkow) for helping
485
493
  confirm issues with GC compaction and mutable strings.
494
+ * Thanks to [Konstantin Haase](https://github.com/rkh) for reporting that
495
+ `RE2::Regexp` shouldn't implement `to_str`.
486
496
 
487
497
  ## Contact
488
498
 
data/ext/re2/extconf.rb CHANGED
@@ -132,38 +132,14 @@ module RE2
132
132
 
133
133
  def build_extension
134
134
  $CFLAGS << " -Wall -Wextra -funroll-loops"
135
- $CXXFLAGS << " -Wall -Wextra -funroll-loops"
135
+ $CXXFLAGS << " -Wall -Wextra -funroll-loops -std=c++17"
136
136
 
137
137
  # Pass -x c++ to force gcc to compile the test program
138
138
  # as C++ (as it will end in .c by default).
139
- compile_options = +"-x c++"
139
+ compile_options = +"-x c++ -std=c++17"
140
140
 
141
141
  have_library("stdc++")
142
142
 
143
- minimal_program = <<~SRC
144
- #include <re2/re2.h>
145
- int main() { return 0; }
146
- SRC
147
-
148
- re2_requires_version_flag = checking_for("re2 that requires explicit C++ version flag") do
149
- !try_compile(minimal_program, compile_options)
150
- end
151
-
152
- if re2_requires_version_flag
153
- # Recent versions of RE2 depend directly on Abseil, which requires a
154
- # compiler with C++17 support.
155
- abort "Cannot compile re2 with your compiler: recent versions require C++17 support." unless %w[c++20 c++17 c++11].any? do |std|
156
- checking_for("re2 that compiles with #{std} standard") do
157
- if try_compile(minimal_program, compile_options + " -std=#{std}")
158
- compile_options << " -std=#{std}"
159
- $CPPFLAGS << " -std=#{std}"
160
-
161
- true
162
- end
163
- end
164
- end
165
- end
166
-
167
143
  # Determine which version of re2 the user has installed.
168
144
  # Revision d9f8806c004d added an `endpos` argument to the
169
145
  # generic Match() function.
data/ext/re2/re2.cc CHANGED
@@ -42,6 +42,7 @@ typedef struct {
42
42
 
43
43
  typedef struct {
44
44
  RE2::Set *set;
45
+ bool compiled;
45
46
  } re2_set;
46
47
 
47
48
  struct nogvl_match_arg {
@@ -170,6 +171,13 @@ static void *nogvl_extract(void *ptr) {
170
171
  return nullptr;
171
172
  }
172
173
 
174
+ static std::string group_name(VALUE key) {
175
+ VALUE str = SYMBOL_P(key) ? rb_sym2str(key) : key;
176
+ str = rb_str_conv_enc(str, rb_enc_get(str), rb_utf8_encoding());
177
+
178
+ return std::string(RSTRING_PTR(str), RSTRING_LEN(str));
179
+ }
180
+
173
181
  VALUE re2_mRE2, re2_cRegexp, re2_cMatchData, re2_cScanner, re2_cSet,
174
182
  re2_eSetMatchError, re2_eSetUnsupportedError, re2_eRegexpUnsupportedError;
175
183
 
@@ -412,9 +420,9 @@ static re2_scanner *unwrap_re2_scanner(VALUE self) {
412
420
  * in alphabetical order rather than definition order, as RE2 stores named
413
421
  * groups internally in a sorted map.
414
422
  *
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).
423
+ * Note RE2 stores capturing group names as UTF-8, even if the {RE2::Regexp}
424
+ * sets the `:utf8` option to false so strings will always be returned in
425
+ * UTF-8.
418
426
  *
419
427
  * @return [Array<String>] an array of names of named capturing groups
420
428
  * @example
@@ -427,9 +435,7 @@ static VALUE re2_regexp_names(const VALUE self) {
427
435
  VALUE names = rb_ary_new2(groups.size());
428
436
 
429
437
  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()));
438
+ rb_ary_push(names, rb_utf8_str_new(group.first.data(), group.first.size()));
433
439
  }
434
440
 
435
441
  return names;
@@ -523,6 +529,8 @@ static VALUE re2_scanner_rewind(VALUE self) {
523
529
  }
524
530
 
525
531
  static VALUE re2_scanner_initialize_copy(VALUE self, VALUE other) {
532
+ if (self == other) return self;
533
+
526
534
  re2_scanner *self_c;
527
535
  re2_scanner *other_c = unwrap_re2_scanner(other);
528
536
 
@@ -646,20 +654,13 @@ static re2::StringPiece *re2_matchdata_find_match(VALUE idx, const VALUE self) {
646
654
 
647
655
  if (RB_INTEGER_TYPE_P(idx)) {
648
656
  id = NUM2INT(idx);
649
- } else if (SYMBOL_P(idx)) {
650
- const auto& groups = p->pattern->NamedCapturingGroups();
651
- auto search = groups.find(rb_id2name(SYM2ID(idx)));
652
-
653
- if (search != groups.end()) {
654
- id = search->second;
655
- } else {
656
- return nullptr;
657
- }
658
657
  } else {
659
- StringValue(idx);
658
+ if (!SYMBOL_P(idx)) {
659
+ StringValue(idx);
660
+ }
660
661
 
661
662
  const auto& groups = p->pattern->NamedCapturingGroups();
662
- auto search = groups.find(std::string(RSTRING_PTR(idx), RSTRING_LEN(idx)));
663
+ auto search = groups.find(group_name(idx));
663
664
 
664
665
  if (search != groups.end()) {
665
666
  id = search->second;
@@ -712,11 +713,11 @@ static VALUE re2_matchdata_begin(const VALUE self, VALUE n) {
712
713
  re2::StringPiece *match = re2_matchdata_find_match(n, self);
713
714
  if (match == nullptr) {
714
715
  return Qnil;
715
- } else {
716
- long offset = match->data() - RSTRING_PTR(m->text);
717
-
718
- return LONG2NUM(rb_str_sublen(m->text, offset));
719
716
  }
717
+
718
+ long offset = match->data() - RSTRING_PTR(m->text);
719
+
720
+ return LONG2NUM(rb_str_sublen(m->text, offset));
720
721
  }
721
722
 
722
723
  /*
@@ -737,11 +738,11 @@ static VALUE re2_matchdata_end(const VALUE self, VALUE n) {
737
738
  re2::StringPiece *match = re2_matchdata_find_match(n, self);
738
739
  if (match == nullptr) {
739
740
  return Qnil;
740
- } else {
741
- long offset = (match->data() - RSTRING_PTR(m->text)) + match->size();
742
-
743
- return LONG2NUM(rb_str_sublen(m->text, offset));
744
741
  }
742
+
743
+ long offset = (match->data() - RSTRING_PTR(m->text)) + match->size();
744
+
745
+ return LONG2NUM(rb_str_sublen(m->text, offset));
745
746
  }
746
747
 
747
748
  /*
@@ -829,6 +830,85 @@ static VALUE re2_matchdata_offset(const VALUE self, VALUE n) {
829
830
  return array;
830
831
  }
831
832
 
833
+ /*
834
+ * Returns the byte offset of the start of the nth element of the
835
+ * {RE2::MatchData}, like Ruby's `MatchData#bytebegin`. This is cheaper than
836
+ * {RE2::MatchData#begin} as it does not need to count characters.
837
+ *
838
+ * @param [Integer, String, Symbol] n the name or number of the submatch
839
+ * @return [Integer, nil] the byte offset of the start of the match or `nil` if
840
+ * there is no such submatch
841
+ * @example
842
+ * m = RE2::Regexp.new('(Ruby)').partial_match("I ♥ Ruby")
843
+ * m.bytebegin(0) #=> 6
844
+ * m.begin(0) #=> 4
845
+ */
846
+ static VALUE re2_matchdata_bytebegin(const VALUE self, VALUE n) {
847
+ re2_matchdata *m = unwrap_re2_matchdata(self);
848
+
849
+ re2::StringPiece *match = re2_matchdata_find_match(n, self);
850
+ if (match == nullptr) {
851
+ return Qnil;
852
+ }
853
+
854
+ return LONG2NUM(match->data() - RSTRING_PTR(m->text));
855
+ }
856
+
857
+ /*
858
+ * Returns the byte offset of the byte following the end of the nth element of
859
+ * the {RE2::MatchData}, like Ruby's `MatchData#byteend`. This is cheaper than
860
+ * {RE2::MatchData#end} as it does not need to count characters.
861
+ *
862
+ * @param [Integer, String, Symbol] n the name or number of the match
863
+ * @return [Integer, nil] the byte offset of the byte following the end of the
864
+ * match or `nil` if there is no such match
865
+ * @example
866
+ * m = RE2::Regexp.new('(Ruby)').partial_match("I ♥ Ruby")
867
+ * m.byteend(0) #=> 10
868
+ * m.end(0) #=> 8
869
+ */
870
+ static VALUE re2_matchdata_byteend(const VALUE self, VALUE n) {
871
+ re2_matchdata *m = unwrap_re2_matchdata(self);
872
+
873
+ re2::StringPiece *match = re2_matchdata_find_match(n, self);
874
+ if (match == nullptr) {
875
+ return Qnil;
876
+ }
877
+
878
+ return LONG2NUM((match->data() - RSTRING_PTR(m->text)) + match->size());
879
+ }
880
+
881
+ /*
882
+ * Returns a two-element array containing the beginning and ending byte
883
+ * offsets of the nth match, like Ruby's `MatchData#byteoffset`. This is
884
+ * cheaper than {RE2::MatchData#offset} as it does not need to count
885
+ * characters.
886
+ *
887
+ * @param [Integer, String, Symbol] n the name or number of the match
888
+ * @return [Array<Integer>, nil] a two-element array with the beginning and
889
+ * ending byte offsets of the match or `nil` if there is no such match
890
+ * @example
891
+ * m = RE2::Regexp.new('(Ruby)').partial_match("I ♥ Ruby")
892
+ * m.byteoffset(0) #=> [6, 10]
893
+ * m.offset(0) #=> [4, 8]
894
+ */
895
+ static VALUE re2_matchdata_byteoffset(const VALUE self, VALUE n) {
896
+ re2_matchdata *m = unwrap_re2_matchdata(self);
897
+
898
+ re2::StringPiece *match = re2_matchdata_find_match(n, self);
899
+ if (match == nullptr) {
900
+ return Qnil;
901
+ }
902
+
903
+ long start = match->data() - RSTRING_PTR(m->text);
904
+
905
+ VALUE array = rb_ary_new2(2);
906
+ rb_ary_push(array, LONG2NUM(start));
907
+ rb_ary_push(array, LONG2NUM(start + match->size()));
908
+
909
+ return array;
910
+ }
911
+
832
912
  /*
833
913
  * Returns the length of the nth match in characters. This is equivalent to
834
914
  * `m[n].length` but without allocating a new string.
@@ -1004,11 +1084,8 @@ static VALUE re2_matchdata_aref(int argc, VALUE *argv, const VALUE self) {
1004
1084
  VALUE idx, rest;
1005
1085
  rb_scan_args(argc, argv, "11", &idx, &rest);
1006
1086
 
1007
- if (TYPE(idx) == T_STRING) {
1008
- return re2_matchdata_named_match(
1009
- std::string(RSTRING_PTR(idx), RSTRING_LEN(idx)), self);
1010
- } else if (SYMBOL_P(idx)) {
1011
- return re2_matchdata_named_match(rb_id2name(SYM2ID(idx)), self);
1087
+ if (TYPE(idx) == T_STRING || SYMBOL_P(idx)) {
1088
+ return re2_matchdata_named_match(group_name(idx), self);
1012
1089
  } else if (!NIL_P(rest) || !RB_INTEGER_TYPE_P(idx) || NUM2INT(idx) < 0) {
1013
1090
  return rb_ary_aref(argc, argv, re2_matchdata_to_a(self));
1014
1091
  } else {
@@ -1122,6 +1199,7 @@ static VALUE re2_matchdata_deconstruct(const VALUE self) {
1122
1199
  * Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
1123
1200
  * returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
1124
1201
  * {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
1202
+ * Capturing group names will always be returned in UTF-8.
1125
1203
  *
1126
1204
  * @return [Hash] a hash of capturing group names to submatches
1127
1205
  * @param [Array<Symbol>, nil] keys an array of `Symbol` capturing group names
@@ -1151,7 +1229,7 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
1151
1229
  if (NIL_P(keys)) {
1152
1230
  for (const auto& group : groups) {
1153
1231
  rb_hash_aset(capturing_groups,
1154
- ID2SYM(rb_intern2(group.first.data(), group.first.size())),
1232
+ rb_str_intern(rb_utf8_str_new(group.first.data(), group.first.size())),
1155
1233
  re2_matchdata_nth_match(group.second, self));
1156
1234
  }
1157
1235
  } else {
@@ -1161,8 +1239,7 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
1161
1239
  for (int i = 0; i < RARRAY_LEN(keys); ++i) {
1162
1240
  VALUE key = rb_ary_entry(keys, i);
1163
1241
  Check_Type(key, T_SYMBOL);
1164
- const char *name = rb_id2name(SYM2ID(key));
1165
- auto search = groups.find(name);
1242
+ auto search = groups.find(group_name(key));
1166
1243
 
1167
1244
  if (search != groups.end()) {
1168
1245
  rb_hash_aset(capturing_groups, key, re2_matchdata_nth_match(search->second, self));
@@ -1182,6 +1259,7 @@ static VALUE re2_matchdata_deconstruct_keys(const VALUE self, const VALUE keys)
1182
1259
  * Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
1183
1260
  * returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
1184
1261
  * {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
1262
+ * Capturing group names will always be returned in UTF-8.
1185
1263
  *
1186
1264
  * @overload named_captures
1187
1265
  * Returns a hash with string keys.
@@ -1219,12 +1297,9 @@ static VALUE re2_matchdata_named_captures(int argc, VALUE *argv, const VALUE sel
1219
1297
  VALUE result = rb_hash_new();
1220
1298
 
1221
1299
  for (const auto& group : groups) {
1222
- VALUE key;
1300
+ VALUE key = rb_utf8_str_new(group.first.data(), group.first.size());
1223
1301
  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());
1302
+ key = rb_str_intern(key);
1228
1303
  }
1229
1304
  rb_hash_aset(result, key, re2_matchdata_nth_match(group.second, self));
1230
1305
  }
@@ -1237,9 +1312,9 @@ static VALUE re2_matchdata_named_captures(int argc, VALUE *argv, const VALUE sel
1237
1312
  * alphabetical order rather than definition order, as RE2 stores named groups
1238
1313
  * internally in a sorted map.
1239
1314
  *
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).
1315
+ * Note RE2 stores capturing group names as UTF-8, even if the {RE2::Regexp}
1316
+ * sets the `:utf8` option to false so strings will always be returned in
1317
+ * UTF-8.
1243
1318
  *
1244
1319
  * @return [Array<String>] an array of names of named capturing groups
1245
1320
  * @example
@@ -1276,12 +1351,8 @@ static VALUE re2_matchdata_values_at(int argc, VALUE *argv, const VALUE self) {
1276
1351
  for (int i = 0; i < argc; ++i) {
1277
1352
  VALUE idx = argv[i];
1278
1353
 
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));
1354
+ if (TYPE(idx) == T_STRING || SYMBOL_P(idx)) {
1355
+ rb_ary_push(result, re2_matchdata_named_match(group_name(idx), self));
1285
1356
  } else {
1286
1357
  rb_ary_push(result, re2_matchdata_nth_match(NUM2INT(idx), self));
1287
1358
  }
@@ -1291,6 +1362,8 @@ static VALUE re2_matchdata_values_at(int argc, VALUE *argv, const VALUE self) {
1291
1362
  }
1292
1363
 
1293
1364
  static VALUE re2_matchdata_initialize_copy(VALUE self, VALUE other) {
1365
+ if (self == other) return self;
1366
+
1294
1367
  re2_matchdata *self_m;
1295
1368
  re2_matchdata *other_m = unwrap_re2_matchdata(other);
1296
1369
 
@@ -1376,24 +1449,17 @@ static VALUE re2_regexp_initialize(int argc, VALUE *argv, VALUE self) {
1376
1449
 
1377
1450
  TypedData_Get_Struct(self, re2_pattern, &re2_regexp_data_type, p);
1378
1451
 
1379
- rb_check_frozen(self);
1380
-
1381
- if (p->pattern) {
1382
- delete p->pattern;
1383
- p->pattern = nullptr;
1384
- }
1452
+ RE2::Options re2_options;
1385
1453
 
1386
1454
  if (RTEST(options)) {
1387
- RE2::Options re2_options;
1388
1455
  parse_re2_options(&re2_options, options);
1389
-
1390
- p->pattern = new(std::nothrow) RE2(
1391
- re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)), re2_options);
1392
- } else {
1393
- p->pattern = new(std::nothrow) RE2(
1394
- re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)));
1395
1456
  }
1396
1457
 
1458
+ rb_check_frozen(self);
1459
+
1460
+ p->pattern = new(std::nothrow) RE2(
1461
+ re2::StringPiece(RSTRING_PTR(pattern), RSTRING_LEN(pattern)), re2_options);
1462
+
1397
1463
  if (p->pattern == nullptr) {
1398
1464
  rb_raise(rb_eNoMemError, "not enough memory to allocate RE2 object");
1399
1465
  }
@@ -1778,9 +1844,9 @@ static VALUE re2_regexp_number_of_capturing_groups(const VALUE self) {
1778
1844
  /*
1779
1845
  * Returns a hash of names to capturing indices of groups.
1780
1846
  *
1781
- * Note RE2 only supports UTF-8 and ISO-8859-1 encoding so strings will be
1782
- * returned in UTF-8 by default or ISO-8859-1 if the `:utf8` option for the
1783
- * {RE2::Regexp} is set to `false` (any other encoding's behaviour is undefined).
1847
+ * Note RE2 stores capturing group names as UTF-8, even if the {RE2::Regexp}
1848
+ * sets the `:utf8` option to false so strings will always be returned in
1849
+ * UTF-8.
1784
1850
  *
1785
1851
  * @return [Hash] a hash of names to capturing indices
1786
1852
  */
@@ -1791,8 +1857,7 @@ static VALUE re2_regexp_named_capturing_groups(const VALUE self) {
1791
1857
 
1792
1858
  for (const auto& group : groups) {
1793
1859
  rb_hash_aset(capturing_groups,
1794
- encoded_str_new(group.first.data(), group.first.size(),
1795
- p->pattern->options().encoding()),
1860
+ rb_utf8_str_new(group.first.data(), group.first.size()),
1796
1861
  INT2FIX(group.second));
1797
1862
  }
1798
1863
 
@@ -1818,7 +1883,7 @@ static VALUE re2_regexp_named_capturing_groups(const VALUE self) {
1818
1883
  * Returns either `true` or `false` indicating whether a successful match was
1819
1884
  * made if the pattern contains no capturing groups.
1820
1885
  *
1821
- * @param [String] text the text to search
1886
+ * @param [String, Symbol, nil] text the text to search; `nil` returns `nil`
1822
1887
  * @return [RE2::MatchData, nil] if the pattern contains capturing groups
1823
1888
  * @return [Boolean] if the pattern does not contain capturing groups
1824
1889
  * @raise [NoMemoryError] if there was not enough memory to allocate the submatches
@@ -1836,7 +1901,7 @@ static VALUE re2_regexp_named_capturing_groups(const VALUE self) {
1836
1901
  * specific number of submatches to extract (padded with `nil`s if
1837
1902
  * necessary).
1838
1903
  *
1839
- * @param [String] text the text to search
1904
+ * @param [String, Symbol, nil] text the text to search; `nil` returns `nil`
1840
1905
  * @param [Hash] options the options with which to perform the match
1841
1906
  * @option options [Integer] :startpos (0) offset at which to start matching
1842
1907
  * @option options [Integer] :endpos offset at which to stop matching, defaults to the text length
@@ -1869,7 +1934,7 @@ static VALUE re2_regexp_named_capturing_groups(const VALUE self) {
1869
1934
  * @deprecated Legacy syntax for matching against `text` with a specific
1870
1935
  * number of submatches to extract. Use `match(text, submatches: n)` instead.
1871
1936
  *
1872
- * @param [String] text the text to search
1937
+ * @param [String, Symbol, nil] text the text to search; `nil` returns `nil`
1873
1938
  * @param [Integer] submatches the number of submatches to extract
1874
1939
  * @return [RE2::MatchData, nil] if extracting any submatches
1875
1940
  * @return [Boolean] if not extracting any submatches
@@ -1888,6 +1953,13 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
1888
1953
 
1889
1954
  rb_scan_args(argc, argv, "11", &text, &options);
1890
1955
 
1956
+ if (NIL_P(text)) {
1957
+ return Qnil;
1958
+ }
1959
+ if (SYMBOL_P(text)) {
1960
+ text = rb_sym2str(text);
1961
+ }
1962
+
1891
1963
  /* Coerce and freeze text to prevent mutation. */
1892
1964
  StringValue(text);
1893
1965
  text = rb_str_new_frozen(text);
@@ -2034,11 +2106,17 @@ static VALUE re2_regexp_match(int argc, VALUE *argv, const VALUE self) {
2034
2106
  * {https://github.com/google/re2/blob/bc0faab533e2b27b85b8ad312abf061e33ed6b5d/re2/re2.h#L413-L427
2035
2107
  * `PartialMatch`}.
2036
2108
  *
2037
- * @param [String] text the text to search
2109
+ * @param [String, Symbol, nil] text the text to search; `nil` returns `false`
2038
2110
  * @return [Boolean] whether the match was successful
2039
2111
  * @raise [TypeError] if text cannot be coerced to a `String`
2040
2112
  */
2041
2113
  static VALUE re2_regexp_match_p(const VALUE self, VALUE text) {
2114
+ if (NIL_P(text)) {
2115
+ return Qfalse;
2116
+ }
2117
+ if (SYMBOL_P(text)) {
2118
+ text = rb_sym2str(text);
2119
+ }
2042
2120
  StringValue(text);
2043
2121
  text = rb_str_new_frozen(text);
2044
2122
 
@@ -2055,11 +2133,17 @@ static VALUE re2_regexp_match_p(const VALUE self, VALUE text) {
2055
2133
  * {https://github.com/google/re2/blob/bc0faab533e2b27b85b8ad312abf061e33ed6b5d/re2/re2.h#L376-L411
2056
2134
  * `FullMatch`}.
2057
2135
  *
2058
- * @param [String] text the text to search
2136
+ * @param [String, Symbol, nil] text the text to search; `nil` returns `false`
2059
2137
  * @return [Boolean] whether the match was successful
2060
2138
  * @raise [TypeError] if text cannot be coerced to a `String`
2061
2139
  */
2062
2140
  static VALUE re2_regexp_full_match_p(const VALUE self, VALUE text) {
2141
+ if (NIL_P(text)) {
2142
+ return Qfalse;
2143
+ }
2144
+ if (SYMBOL_P(text)) {
2145
+ text = rb_sym2str(text);
2146
+ }
2063
2147
  StringValue(text);
2064
2148
  text = rb_str_new_frozen(text);
2065
2149
 
@@ -2503,9 +2587,12 @@ static VALUE re2_set_initialize(int argc, VALUE *argv, VALUE self) {
2503
2587
 
2504
2588
  rb_check_frozen(self);
2505
2589
 
2590
+ /* Prevent re-initialisation: #match releases the GVL while holding a
2591
+ * pointer to s->set, so replacing s->set from another thread would free
2592
+ * the object out from under a concurrent match.
2593
+ */
2506
2594
  if (s->set) {
2507
- delete s->set;
2508
- s->set = nullptr;
2595
+ rb_raise(rb_eTypeError, "already initialized RE2::Set");
2509
2596
  }
2510
2597
 
2511
2598
  s->set = new(std::nothrow) RE2::Set(re2_options, re2_anchor);
@@ -2570,6 +2657,7 @@ static VALUE re2_set_compile(VALUE self) {
2570
2657
  bool compiled = s->set->Compile();
2571
2658
 
2572
2659
  if (compiled) {
2660
+ s->compiled = true;
2573
2661
  rb_obj_freeze(self);
2574
2662
  }
2575
2663
 
@@ -2687,6 +2775,11 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
2687
2775
 
2688
2776
  if (raise_exception) {
2689
2777
  #ifdef HAVE_ERROR_INFO_ARGUMENT
2778
+ /* RE2::Set::Match dereferences a null program after a failed Compile. */
2779
+ if (!s->compiled) {
2780
+ rb_raise(re2_eSetMatchError, "#match must not be called before #compile");
2781
+ }
2782
+
2690
2783
  RE2::Set::ErrorInfo e;
2691
2784
  nogvl_set_match_arg arg;
2692
2785
  arg.set = s->set;
@@ -2729,6 +2822,10 @@ static VALUE re2_set_match(int argc, VALUE *argv, const VALUE self) {
2729
2822
  rb_raise(re2_eSetUnsupportedError, "current version of RE2::Set::Match() does not output error information, :exception option can only be set to false");
2730
2823
  #endif
2731
2824
  } else {
2825
+ if (!s->compiled) {
2826
+ return rb_ary_new();
2827
+ }
2828
+
2732
2829
  nogvl_set_match_arg arg;
2733
2830
  arg.set = s->set;
2734
2831
  arg.text = re2::StringPiece(RSTRING_PTR(str), RSTRING_LEN(str));
@@ -2801,6 +2898,12 @@ extern "C" void Init_re2(void) {
2801
2898
  RUBY_METHOD_FUNC(re2_matchdata_post_match), 0);
2802
2899
  rb_define_method(re2_cMatchData, "offset",
2803
2900
  RUBY_METHOD_FUNC(re2_matchdata_offset), 1);
2901
+ rb_define_method(re2_cMatchData, "bytebegin",
2902
+ RUBY_METHOD_FUNC(re2_matchdata_bytebegin), 1);
2903
+ rb_define_method(re2_cMatchData, "byteend",
2904
+ RUBY_METHOD_FUNC(re2_matchdata_byteend), 1);
2905
+ rb_define_method(re2_cMatchData, "byteoffset",
2906
+ RUBY_METHOD_FUNC(re2_matchdata_byteoffset), 1);
2804
2907
  rb_define_method(re2_cMatchData, "match_length",
2805
2908
  RUBY_METHOD_FUNC(re2_matchdata_match_length), 1);
2806
2909
  rb_define_method(re2_cMatchData, "[]", RUBY_METHOD_FUNC(re2_matchdata_aref),
@@ -2867,14 +2970,11 @@ extern "C" void Init_re2(void) {
2867
2970
  rb_define_method(re2_cRegexp, "partial_match?",
2868
2971
  RUBY_METHOD_FUNC(re2_regexp_match_p), 1);
2869
2972
  rb_define_method(re2_cRegexp, "=~", RUBY_METHOD_FUNC(re2_regexp_match_p), 1);
2870
- rb_define_method(re2_cRegexp, "===", RUBY_METHOD_FUNC(re2_regexp_match_p), 1);
2871
2973
  rb_define_method(re2_cRegexp, "full_match?",
2872
2974
  RUBY_METHOD_FUNC(re2_regexp_full_match_p), 1);
2873
2975
  rb_define_method(re2_cRegexp, "scan",
2874
2976
  RUBY_METHOD_FUNC(re2_regexp_scan), 1);
2875
2977
  rb_define_method(re2_cRegexp, "to_s", RUBY_METHOD_FUNC(re2_regexp_to_s), 0);
2876
- rb_define_method(re2_cRegexp, "to_str", RUBY_METHOD_FUNC(re2_regexp_to_s),
2877
- 0);
2878
2978
  rb_define_method(re2_cRegexp, "pattern", RUBY_METHOD_FUNC(re2_regexp_to_s),
2879
2979
  0);
2880
2980
  rb_define_method(re2_cRegexp, "source", RUBY_METHOD_FUNC(re2_regexp_to_s),
data/lib/3.1/re2.so CHANGED
Binary file
data/lib/3.2/re2.so CHANGED
Binary file
data/lib/3.3/re2.so CHANGED
Binary file
data/lib/3.4/re2.so CHANGED
Binary file
data/lib/4.0/re2.so CHANGED
Binary file
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # re2 (https://github.com/mudge/re2)
4
+ # Ruby bindings to RE2, a "fast, safe, thread-friendly alternative to
5
+ # backtracking regular expression engines like those used in PCRE, Perl, and
6
+ # Python".
7
+ #
8
+ # Copyright (c) 2010, Paul Mucur (https://mudge.name)
9
+ # Released under the BSD Licence, please see LICENSE.txt
10
+
11
+
12
+ module RE2
13
+ class MatchData
14
+ # Return whether this match has the same regular expression, text, and
15
+ # submatch offsets as another {RE2::MatchData}, like Ruby's own
16
+ # `MatchData#==`.
17
+ #
18
+ # @param [Object] other the object to compare against
19
+ # @return [Boolean] whether the two matches are equal
20
+ # @example
21
+ # re = RE2::Regexp.new('(\d+)')
22
+ # re.match("abc 123") == re.match("abc 123") #=> true
23
+ # re.match("abc 123") == re.match("123 abc") #=> false
24
+ def ==(other)
25
+ other.is_a?(RE2::MatchData) &&
26
+ regexp == other.regexp &&
27
+ string == other.string &&
28
+ byteoffsets == other.byteoffsets
29
+ end
30
+
31
+ alias_method :eql?, :==
32
+
33
+ # Return a hash based on the regular expression, text, and submatch offsets
34
+ # so that equal {RE2::MatchData}s can be used as Hash keys.
35
+ #
36
+ # @return [Integer] the hash of the match
37
+ def hash
38
+ [regexp, string, byteoffsets].hash
39
+ end
40
+
41
+ protected
42
+
43
+ def byteoffsets
44
+ Array.new(size) { |i| byteoffset(i) }
45
+ end
46
+ end
47
+ end