re2 2.26.2 → 2.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +5 -0
- data/README.md +1 -1
- data/ext/re2/extconf.rb +2 -26
- data/ext/re2/re2.cc +364 -155
- data/lib/re2/version.rb +1 -1
- data/re2.gemspec +1 -1
- data/spec/re2/match_data_spec.rb +90 -2
- data/spec/re2/regexp_spec.rb +85 -5
- data/spec/re2/scanner_spec.rb +9 -0
- data/spec/re2/set_spec.rb +70 -4
- data/spec/re2_spec.rb +60 -0
- metadata +4 -4
data/lib/re2/version.rb
CHANGED
data/re2.gemspec
CHANGED
|
@@ -41,7 +41,7 @@ Gem::Specification.new do |s|
|
|
|
41
41
|
"spec/re2/scanner_spec.rb"
|
|
42
42
|
]
|
|
43
43
|
s.add_development_dependency("rake-compiler", "~> 1.3.1")
|
|
44
|
-
s.add_development_dependency("rake-compiler-dock", "~> 1.
|
|
44
|
+
s.add_development_dependency("rake-compiler-dock", "~> 1.12.0")
|
|
45
45
|
s.add_development_dependency("rspec", "~> 3.2")
|
|
46
46
|
s.add_runtime_dependency("mini_portile2", "~> 2.8.9") # keep version in sync with extconf.rb
|
|
47
47
|
end
|
data/spec/re2/match_data_spec.rb
CHANGED
|
@@ -59,6 +59,15 @@ RSpec.describe RE2::MatchData do
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
describe "#initialize_copy" do
|
|
63
|
+
it "is a no-op when the source is the same object" do
|
|
64
|
+
md = RE2::Regexp.new('(\d+)').match("123")
|
|
65
|
+
md.send(:initialize_copy, md)
|
|
66
|
+
|
|
67
|
+
expect(md.to_a).to eq(["123", "123"])
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
62
71
|
describe "#to_a" do
|
|
63
72
|
it "is populated with the match and capturing groups" do
|
|
64
73
|
a = RE2::Regexp.new('w(o)(o)').match('woo').to_a
|
|
@@ -154,6 +163,24 @@ RSpec.describe RE2::MatchData do
|
|
|
154
163
|
expect(md[:numbers]).to eq("123")
|
|
155
164
|
end
|
|
156
165
|
|
|
166
|
+
it "allows access using a UTF-8 name and a UTF-8 pattern" do
|
|
167
|
+
re = RE2::Regexp.new('(?P<café>\d+)', log_errors: false)
|
|
168
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
169
|
+
|
|
170
|
+
md = re.match('bob 123')
|
|
171
|
+
|
|
172
|
+
expect(md[:café]).to eq("123")
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
it "allows access using a Latin-1 name and a Latin-1 pattern" do
|
|
176
|
+
re = RE2::Regexp.new('(?P<café>\d+)'.encode("ISO-8859-1"), utf8: false, log_errors: false)
|
|
177
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
178
|
+
|
|
179
|
+
md = re.match('bob 123')
|
|
180
|
+
|
|
181
|
+
expect(md["café".encode("ISO-8859-1")]).to eq("123")
|
|
182
|
+
end
|
|
183
|
+
|
|
157
184
|
it "allows access by names and indices with mixed groups", :aggregate_failures do
|
|
158
185
|
md = RE2::Regexp.new('(?P<name>\w+)(\s*)(?P<numbers>\d+)').match("bob 123")
|
|
159
186
|
|
|
@@ -199,6 +226,12 @@ RSpec.describe RE2::MatchData do
|
|
|
199
226
|
expect(md[:name].encoding.name).to eq("ISO-8859-1")
|
|
200
227
|
end
|
|
201
228
|
|
|
229
|
+
it "handles null bytes in symbol names" do
|
|
230
|
+
md = RE2::Regexp.new('(?P<name>\S+)').match("bob")
|
|
231
|
+
|
|
232
|
+
expect(md[:"name\0b"]).to be_nil
|
|
233
|
+
end
|
|
234
|
+
|
|
202
235
|
it "supports GC compaction" do
|
|
203
236
|
md = RE2::Regexp.new('(wo{2})').match('woohoo' * 5)
|
|
204
237
|
GC.compact
|
|
@@ -358,6 +391,24 @@ RSpec.describe RE2::MatchData do
|
|
|
358
391
|
expect(md.string[md.begin(:foo)..-1]).to eq('foobar')
|
|
359
392
|
end
|
|
360
393
|
|
|
394
|
+
it "returns the offset of the start of a match by non-ASCII symbol name" do
|
|
395
|
+
re = RE2::Regexp.new('(?P<café>fo{2})', log_errors: false)
|
|
396
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
397
|
+
|
|
398
|
+
md = re.match('a foobar')
|
|
399
|
+
|
|
400
|
+
expect(md.string[md.begin(:café)..-1]).to eq('foobar')
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
it "returns the offset of the start of a match by Latin-1 string name" do
|
|
404
|
+
re = RE2::Regexp.new('(?P<café>fo{2})'.encode("ISO-8859-1"), utf8: false, log_errors: false)
|
|
405
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
406
|
+
|
|
407
|
+
md = re.match('a foobar')
|
|
408
|
+
|
|
409
|
+
expect(md.string[md.begin("café".encode("ISO-8859-1"))..-1]).to eq('foobar')
|
|
410
|
+
end
|
|
411
|
+
|
|
361
412
|
it "returns the offset of the start of a match by something that can be coerced to a String" do
|
|
362
413
|
md = RE2::Regexp.new('(?P<foo>fo{2})').match('a foobar')
|
|
363
414
|
|
|
@@ -822,12 +873,33 @@ RSpec.describe RE2::MatchData do
|
|
|
822
873
|
expect(md.named_captures).to eq("empty" => "", "word" => "bob")
|
|
823
874
|
end
|
|
824
875
|
|
|
876
|
+
it "returns UTF-8 names even for Latin-1 patterns" do
|
|
877
|
+
md = RE2::Regexp.new('(?P<numbers>\d+)', utf8: false).match('123')
|
|
878
|
+
key = md.named_captures.keys.first
|
|
879
|
+
|
|
880
|
+
expect(key.encoding).to eq(Encoding::UTF_8)
|
|
881
|
+
end
|
|
882
|
+
|
|
825
883
|
it "returns symbol keys when symbolize_names: true" do
|
|
826
884
|
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
827
885
|
|
|
828
886
|
expect(md.named_captures(symbolize_names: true)).to eq(numbers: "123", letters: "abc")
|
|
829
887
|
end
|
|
830
888
|
|
|
889
|
+
it "returns garbage-collectable symbol keys when symbolize_names: true" do
|
|
890
|
+
md = RE2::Regexp.new('(?P<collectable_named_capture>\d+)').match('123')
|
|
891
|
+
key = md.named_captures(symbolize_names: true).keys.first
|
|
892
|
+
|
|
893
|
+
expect(ObjectSpace.each_object(Symbol)).to include(key)
|
|
894
|
+
end
|
|
895
|
+
|
|
896
|
+
it "correctly encodes non-ASCII capturing group names as symbols" do
|
|
897
|
+
re = RE2::Regexp.new('(?P<café>\d+)', log_errors: false)
|
|
898
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
899
|
+
|
|
900
|
+
expect(re.match('123').named_captures(symbolize_names: true)).to eq(café: "123")
|
|
901
|
+
end
|
|
902
|
+
|
|
831
903
|
it "returns string keys when symbolize_names: false" do
|
|
832
904
|
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
833
905
|
|
|
@@ -858,10 +930,10 @@ RSpec.describe RE2::MatchData do
|
|
|
858
930
|
expect(md.names.first.encoding).to eq(Encoding::UTF_8)
|
|
859
931
|
end
|
|
860
932
|
|
|
861
|
-
it "returns
|
|
933
|
+
it "returns UTF-8 strings if the pattern is not UTF-8" do
|
|
862
934
|
md = RE2::Regexp.new('(?P<numbers>\d+)', utf8: false).match('123')
|
|
863
935
|
|
|
864
|
-
expect(md.names.first.encoding).to eq(Encoding::
|
|
936
|
+
expect(md.names.first.encoding).to eq(Encoding::UTF_8)
|
|
865
937
|
end
|
|
866
938
|
|
|
867
939
|
it "raises an error when called on an uninitialized object" do
|
|
@@ -876,6 +948,22 @@ RSpec.describe RE2::MatchData do
|
|
|
876
948
|
expect(md.deconstruct_keys(nil)).to eq(numbers: '123', letters: 'abc')
|
|
877
949
|
end
|
|
878
950
|
|
|
951
|
+
it "returns garbage-collectable symbol keys if given nil" do
|
|
952
|
+
md = RE2::Regexp.new('(?P<collectable_deconstructed_key>\d+)').match('123')
|
|
953
|
+
key = md.deconstruct_keys(nil).keys.first
|
|
954
|
+
|
|
955
|
+
expect(ObjectSpace.each_object(Symbol)).to include(key)
|
|
956
|
+
end
|
|
957
|
+
|
|
958
|
+
it "returns UTF-8 named captures for a Latin-1 pattern if given nil" do
|
|
959
|
+
re = RE2::Regexp.new('(?P<café>\d+) (?P<letters>[a-zA-Z]+)'.encode("ISO-8859-1"), utf8: false, log_errors: false)
|
|
960
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
961
|
+
|
|
962
|
+
md = re.match('123 abc')
|
|
963
|
+
|
|
964
|
+
expect(md.deconstruct_keys(nil)).to eq(café: '123', letters: 'abc')
|
|
965
|
+
end
|
|
966
|
+
|
|
879
967
|
it "returns only named captures if given names" do
|
|
880
968
|
md = RE2::Regexp.new('(?P<numbers>\d+) (?P<letters>[a-zA-Z]+)').match('123 abc')
|
|
881
969
|
|
data/spec/re2/regexp_spec.rb
CHANGED
|
@@ -39,6 +39,23 @@ RSpec.describe RE2::Regexp do
|
|
|
39
39
|
|
|
40
40
|
expect(re).to be_a(RE2::Regexp)
|
|
41
41
|
end
|
|
42
|
+
|
|
43
|
+
it "returns a frozen object" do
|
|
44
|
+
expect(RE2::Regexp.new('woo')).to be_frozen
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "cannot be re-initialized" do
|
|
48
|
+
re = RE2::Regexp.new('woo')
|
|
49
|
+
|
|
50
|
+
expect { re.send(:initialize, 'bar') }.to raise_error(FrozenError)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "cannot be re-initialized while parsing options" do
|
|
54
|
+
re = described_class.allocate
|
|
55
|
+
options = Hash.new { |_, key| re.send(:initialize, 'bar') if key == :utf8 }
|
|
56
|
+
|
|
57
|
+
expect { re.send(:initialize, 'woo', options) }.to raise_error(FrozenError)
|
|
58
|
+
end
|
|
42
59
|
end
|
|
43
60
|
|
|
44
61
|
describe "#dup" do
|
|
@@ -70,6 +87,13 @@ RSpec.describe RE2::Regexp do
|
|
|
70
87
|
expect(copy).to_not be_case_sensitive
|
|
71
88
|
end
|
|
72
89
|
|
|
90
|
+
it "returns a frozen copy" do
|
|
91
|
+
re = described_class.new('(\d+)')
|
|
92
|
+
copy = re.dup
|
|
93
|
+
|
|
94
|
+
expect(copy).to be_frozen
|
|
95
|
+
end
|
|
96
|
+
|
|
73
97
|
it "raises an error when called on an uninitialized object" do
|
|
74
98
|
expect { described_class.allocate.dup }.to raise_error(TypeError, /uninitialized RE2::Regexp/)
|
|
75
99
|
end
|
|
@@ -83,6 +107,13 @@ RSpec.describe RE2::Regexp do
|
|
|
83
107
|
expect(copy.to_s).to eq('woo')
|
|
84
108
|
end
|
|
85
109
|
|
|
110
|
+
it "returns a frozen copy" do
|
|
111
|
+
re = described_class.new('woo')
|
|
112
|
+
copy = re.clone
|
|
113
|
+
|
|
114
|
+
expect(copy).to be_frozen
|
|
115
|
+
end
|
|
116
|
+
|
|
86
117
|
it "raises an error when called on an uninitialized object" do
|
|
87
118
|
expect { described_class.allocate.clone }.to raise_error(TypeError, /uninitialized RE2::Regexp/)
|
|
88
119
|
end
|
|
@@ -769,6 +800,16 @@ RSpec.describe RE2::Regexp do
|
|
|
769
800
|
it "raises an error when called on an uninitialized object" do
|
|
770
801
|
expect { described_class.allocate.match("test") }.to raise_error(TypeError, /uninitialized RE2::Regexp/)
|
|
771
802
|
end
|
|
803
|
+
|
|
804
|
+
it "can be run concurrently" do
|
|
805
|
+
re = RE2::Regexp.new('(\w+)\s(\w+)')
|
|
806
|
+
|
|
807
|
+
threads = 10.times.map do
|
|
808
|
+
Thread.new { re.match("one two").values_at(1, 2) }
|
|
809
|
+
end
|
|
810
|
+
|
|
811
|
+
expect(threads.map(&:value)).to all(eq(["one", "two"]))
|
|
812
|
+
end
|
|
772
813
|
end
|
|
773
814
|
|
|
774
815
|
describe "#match?" do
|
|
@@ -794,6 +835,16 @@ RSpec.describe RE2::Regexp do
|
|
|
794
835
|
it "raises an error when called on an uninitialized object" do
|
|
795
836
|
expect { described_class.allocate.match?("test") }.to raise_error(TypeError, /uninitialized RE2::Regexp/)
|
|
796
837
|
end
|
|
838
|
+
|
|
839
|
+
it "can be run concurrently" do
|
|
840
|
+
re = RE2::Regexp.new('(\w+)\s(\w+)')
|
|
841
|
+
|
|
842
|
+
threads = 10.times.map do
|
|
843
|
+
Thread.new { re.match?("one two") }
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
expect(threads.map(&:value)).to all(eq(true))
|
|
847
|
+
end
|
|
797
848
|
end
|
|
798
849
|
|
|
799
850
|
describe "#partial_match?" do
|
|
@@ -826,6 +877,16 @@ RSpec.describe RE2::Regexp do
|
|
|
826
877
|
it "raises an error when called on an uninitialized object" do
|
|
827
878
|
expect { described_class.allocate.partial_match?("test") }.to raise_error(TypeError, /uninitialized RE2::Regexp/)
|
|
828
879
|
end
|
|
880
|
+
|
|
881
|
+
it "can be run concurrently" do
|
|
882
|
+
re = RE2::Regexp.new('(\d+)')
|
|
883
|
+
|
|
884
|
+
threads = 10.times.map do
|
|
885
|
+
Thread.new { re.partial_match?("alice 123") }
|
|
886
|
+
end
|
|
887
|
+
|
|
888
|
+
expect(threads.map(&:value)).to all(eq(true))
|
|
889
|
+
end
|
|
829
890
|
end
|
|
830
891
|
|
|
831
892
|
describe "#=~" do
|
|
@@ -915,6 +976,16 @@ RSpec.describe RE2::Regexp do
|
|
|
915
976
|
it "raises an error when called on an uninitialized object" do
|
|
916
977
|
expect { described_class.allocate.full_match?("test") }.to raise_error(TypeError, /uninitialized RE2::Regexp/)
|
|
917
978
|
end
|
|
979
|
+
|
|
980
|
+
it "can be run concurrently" do
|
|
981
|
+
re = RE2::Regexp.new('(\w+) (\d+)')
|
|
982
|
+
|
|
983
|
+
threads = 10.times.map do
|
|
984
|
+
Thread.new { re.full_match?("alice 123") }
|
|
985
|
+
end
|
|
986
|
+
|
|
987
|
+
expect(threads.map(&:value)).to all(eq(true))
|
|
988
|
+
end
|
|
918
989
|
end
|
|
919
990
|
|
|
920
991
|
describe "#ok?" do
|
|
@@ -984,6 +1055,13 @@ RSpec.describe RE2::Regexp do
|
|
|
984
1055
|
expect(RE2::Regexp.new('???', log_errors: false).named_capturing_groups).to be_empty
|
|
985
1056
|
end
|
|
986
1057
|
|
|
1058
|
+
it "returns names as UTF-8 even if the pattern is Latin-1" do
|
|
1059
|
+
groups = RE2::Regexp.new('(?P<bob>a)', utf8: false).named_capturing_groups
|
|
1060
|
+
key = groups.keys.first
|
|
1061
|
+
|
|
1062
|
+
expect(key.encoding).to eq(Encoding::UTF_8)
|
|
1063
|
+
end
|
|
1064
|
+
|
|
987
1065
|
it "raises an error when called on an uninitialized object" do
|
|
988
1066
|
expect { described_class.allocate.named_capturing_groups }.to raise_error(TypeError, /uninitialized RE2::Regexp/)
|
|
989
1067
|
end
|
|
@@ -1007,15 +1085,17 @@ RSpec.describe RE2::Regexp do
|
|
|
1007
1085
|
end
|
|
1008
1086
|
|
|
1009
1087
|
it "returns UTF-8 strings if the pattern is UTF-8" do
|
|
1010
|
-
|
|
1088
|
+
re = RE2::Regexp.new('(?P<café>\d+)', log_errors: false)
|
|
1089
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
1011
1090
|
|
|
1012
|
-
expect(names.first.encoding).to eq(Encoding::UTF_8)
|
|
1091
|
+
expect(re.names.first.encoding).to eq(Encoding::UTF_8)
|
|
1013
1092
|
end
|
|
1014
1093
|
|
|
1015
|
-
it "returns
|
|
1016
|
-
|
|
1094
|
+
it "returns UTF-8 strings if the pattern is not UTF-8" do
|
|
1095
|
+
re = RE2::Regexp.new('(?P<café>a)'.encode("ISO-8859-1"), utf8: false, log_errors: false)
|
|
1096
|
+
skip "Underlying RE2 does not support non-ASCII capturing group names" unless re.ok?
|
|
1017
1097
|
|
|
1018
|
-
expect(names
|
|
1098
|
+
expect(re.names).to contain_exactly("café")
|
|
1019
1099
|
end
|
|
1020
1100
|
|
|
1021
1101
|
it "raises an error when called on an uninitialized object" do
|
data/spec/re2/scanner_spec.rb
CHANGED
|
@@ -106,6 +106,15 @@ RSpec.describe RE2::Scanner do
|
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
describe "#initialize_copy" do
|
|
110
|
+
it "is a no-op when the source is the same object" do
|
|
111
|
+
scanner = RE2::Regexp.new('(\w+)').scan("foo bar")
|
|
112
|
+
scanner.send(:initialize_copy, scanner)
|
|
113
|
+
|
|
114
|
+
expect(scanner.scan).to eq(["foo"])
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
109
118
|
describe "#scan" do
|
|
110
119
|
it "returns the next array of matches", :aggregate_failures do
|
|
111
120
|
r = RE2::Regexp.new('(\w+)')
|
data/spec/re2/set_spec.rb
CHANGED
|
@@ -49,6 +49,13 @@ RSpec.describe RE2::Set do
|
|
|
49
49
|
"anchor should be one of: :unanchored, :anchor_start, :anchor_both"
|
|
50
50
|
)
|
|
51
51
|
end
|
|
52
|
+
|
|
53
|
+
it "cannot be re-initialized before compilation" do
|
|
54
|
+
set = RE2::Set.new
|
|
55
|
+
set.add("abc")
|
|
56
|
+
|
|
57
|
+
expect { set.send(:initialize) }.to raise_error(TypeError, /already initialized RE2::Set/)
|
|
58
|
+
end
|
|
52
59
|
end
|
|
53
60
|
|
|
54
61
|
describe "#dup" do
|
|
@@ -84,14 +91,12 @@ RSpec.describe RE2::Set do
|
|
|
84
91
|
expect { set.add("(?P<#{'o' * 200}") }.to raise_error(ArgumentError, "str rejected by RE2::Set->Add(): invalid named capture group: (?P<oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo")
|
|
85
92
|
end
|
|
86
93
|
|
|
87
|
-
it "raises
|
|
94
|
+
it "raises a FrozenError if called after #compile" do
|
|
88
95
|
set = RE2::Set.new(:unanchored, log_errors: false)
|
|
89
96
|
set.add("abc")
|
|
90
97
|
set.compile
|
|
91
98
|
|
|
92
|
-
|
|
93
|
-
expect { set.add("def") }.to raise_error(ArgumentError)
|
|
94
|
-
end
|
|
99
|
+
expect { set.add("def") }.to raise_error(FrozenError)
|
|
95
100
|
end
|
|
96
101
|
|
|
97
102
|
it "raises an error if given a pattern that can't be coerced to a String" do
|
|
@@ -121,6 +126,29 @@ RSpec.describe RE2::Set do
|
|
|
121
126
|
expect(set.compile).to be_truthy
|
|
122
127
|
end
|
|
123
128
|
|
|
129
|
+
it "freezes the set on successful compilation" do
|
|
130
|
+
set = RE2::Set.new
|
|
131
|
+
set.add("abc")
|
|
132
|
+
set.compile
|
|
133
|
+
|
|
134
|
+
expect(set).to be_frozen
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "is not frozen before compilation" do
|
|
138
|
+
set = RE2::Set.new
|
|
139
|
+
set.add("abc")
|
|
140
|
+
|
|
141
|
+
expect(set).to_not be_frozen
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "cannot be re-initialized after compilation" do
|
|
145
|
+
set = RE2::Set.new
|
|
146
|
+
set.add("abc")
|
|
147
|
+
set.compile
|
|
148
|
+
|
|
149
|
+
expect { set.send(:initialize) }.to raise_error(FrozenError)
|
|
150
|
+
end
|
|
151
|
+
|
|
124
152
|
it "raises an error when called on an uninitialized object" do
|
|
125
153
|
expect { described_class.allocate.compile }.to raise_error(TypeError, /uninitialized RE2::Set/)
|
|
126
154
|
end
|
|
@@ -191,6 +219,30 @@ RSpec.describe RE2::Set do
|
|
|
191
219
|
end
|
|
192
220
|
end
|
|
193
221
|
|
|
222
|
+
it "raises an error if called after a failed #compile" do
|
|
223
|
+
skip "Underlying RE2::Set::Match does not output error information" unless RE2::Set.match_raises_errors?
|
|
224
|
+
|
|
225
|
+
set = RE2::Set.new(:unanchored, max_mem: 1, log_errors: false)
|
|
226
|
+
set.add("a{1000}b{1000}c{1000}")
|
|
227
|
+
|
|
228
|
+
silence_stderr do
|
|
229
|
+
set.compile
|
|
230
|
+
|
|
231
|
+
expect { set.match("abc") }.to raise_error(RE2::Set::MatchError, "#match must not be called before #compile")
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
it "returns an empty array if called after a failed #compile when :exception is false" do
|
|
236
|
+
set = RE2::Set.new(:unanchored, max_mem: 1, log_errors: false)
|
|
237
|
+
set.add("a{1000}b{1000}c{1000}")
|
|
238
|
+
|
|
239
|
+
silence_stderr do
|
|
240
|
+
set.compile
|
|
241
|
+
|
|
242
|
+
expect(set.match("abc", exception: false)).to be_empty
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
194
246
|
it "raises an error if :exception is true and RE2 does not support it" do
|
|
195
247
|
skip "Underlying RE2::Set::Match outputs error information" if RE2::Set.match_raises_errors?
|
|
196
248
|
|
|
@@ -226,6 +278,20 @@ RSpec.describe RE2::Set do
|
|
|
226
278
|
it "raises an error when called on an uninitialized object" do
|
|
227
279
|
expect { described_class.allocate.match("foo") }.to raise_error(TypeError, /uninitialized RE2::Set/)
|
|
228
280
|
end
|
|
281
|
+
|
|
282
|
+
it "can be run concurrently" do
|
|
283
|
+
set = RE2::Set.new
|
|
284
|
+
set.add("abc")
|
|
285
|
+
set.add("def")
|
|
286
|
+
set.add("ghi")
|
|
287
|
+
set.compile
|
|
288
|
+
|
|
289
|
+
threads = 10.times.map do
|
|
290
|
+
Thread.new { set.match("abcdefghi", exception: false) }
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
expect(threads.map(&:value)).to all(eq([0, 1, 2]))
|
|
294
|
+
end
|
|
229
295
|
end
|
|
230
296
|
|
|
231
297
|
describe "#size" do
|
data/spec/re2_spec.rb
CHANGED
|
@@ -93,6 +93,26 @@ RSpec.describe RE2 do
|
|
|
93
93
|
it "raises a Type Error for a replacement that can't be converted to String" do
|
|
94
94
|
expect { RE2.replace("woo", "oo", 0) }.to raise_error(TypeError)
|
|
95
95
|
end
|
|
96
|
+
|
|
97
|
+
it "can be run concurrently with the same RE2::Regexp pattern" do
|
|
98
|
+
re = RE2::Regexp.new('(\w+)\s(\w+)')
|
|
99
|
+
|
|
100
|
+
threads = 10.times.map do
|
|
101
|
+
Thread.new { RE2.replace("one two", re, '\2 \1') }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
expect(threads.map(&:value)).to all(eq("two one"))
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
it "can be run concurrently with the same string pattern" do
|
|
108
|
+
re = '(\w+)\s(\w+)'
|
|
109
|
+
|
|
110
|
+
threads = 10.times.map do
|
|
111
|
+
Thread.new { RE2.replace("one two", re, '\2 \1') }
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
expect(threads.map(&:value)).to all(eq("two one"))
|
|
115
|
+
end
|
|
96
116
|
end
|
|
97
117
|
|
|
98
118
|
describe ".Replace" do
|
|
@@ -193,6 +213,26 @@ RSpec.describe RE2 do
|
|
|
193
213
|
it "raises a Type Error for a replacement that can't be converted to String" do
|
|
194
214
|
expect { RE2.global_replace("woo", "o", 0) }.to raise_error(TypeError)
|
|
195
215
|
end
|
|
216
|
+
|
|
217
|
+
it "can be run concurrently with the same RE2::Regexp pattern" do
|
|
218
|
+
re = RE2::Regexp.new('(\w+)\s(\w+)')
|
|
219
|
+
|
|
220
|
+
threads = 10.times.map do
|
|
221
|
+
Thread.new { RE2.global_replace("one two three four", re, '\2 \1') }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
expect(threads.map(&:value)).to all(eq("two one four three"))
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it "can be run concurrently with the same string pattern" do
|
|
228
|
+
re = '(\w+)\s(\w+)'
|
|
229
|
+
|
|
230
|
+
threads = 10.times.map do
|
|
231
|
+
Thread.new { RE2.global_replace("one two three four", re, '\2 \1') }
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
expect(threads.map(&:value)).to all(eq("two one four three"))
|
|
235
|
+
end
|
|
196
236
|
end
|
|
197
237
|
|
|
198
238
|
describe ".GlobalReplace" do
|
|
@@ -281,6 +321,26 @@ RSpec.describe RE2 do
|
|
|
281
321
|
it "raises a Type Error for a rewrite that can't be converted to String" do
|
|
282
322
|
expect { RE2.extract("woo", '(\w+)', 0) }.to raise_error(TypeError)
|
|
283
323
|
end
|
|
324
|
+
|
|
325
|
+
it "can be run concurrently with the same RE2::Regexp pattern" do
|
|
326
|
+
re = RE2::Regexp.new('(\w+)@(\w+)')
|
|
327
|
+
|
|
328
|
+
threads = 10.times.map do
|
|
329
|
+
Thread.new { RE2.extract("alice@example", re, '\2-\1') }
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
expect(threads.map(&:value)).to all(eq("example-alice"))
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
it "can be run concurrently with the same string pattern" do
|
|
336
|
+
re = '(\w+)@(\w+)'
|
|
337
|
+
|
|
338
|
+
threads = 10.times.map do
|
|
339
|
+
Thread.new { RE2.extract("alice@example", re, '\2-\1') }
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
expect(threads.map(&:value)).to all(eq("example-alice"))
|
|
343
|
+
end
|
|
284
344
|
end
|
|
285
345
|
|
|
286
346
|
describe "#escape" do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: re2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.28.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paul Mucur
|
|
@@ -30,14 +30,14 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.
|
|
33
|
+
version: 1.12.0
|
|
34
34
|
type: :development
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.
|
|
40
|
+
version: 1.12.0
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rspec
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
117
117
|
- !ruby/object:Gem::Version
|
|
118
118
|
version: '0'
|
|
119
119
|
requirements: []
|
|
120
|
-
rubygems_version: 4.0.
|
|
120
|
+
rubygems_version: 4.0.20
|
|
121
121
|
specification_version: 4
|
|
122
122
|
summary: Ruby bindings to RE2.
|
|
123
123
|
test_files:
|