re2 1.17.2 → 1.17.3

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.
package/README.md CHANGED
@@ -347,6 +347,7 @@ console.log('re2_res : ' + re2_res); // prints: re2_res : abc,a,b,c
347
347
 
348
348
  ## Release history
349
349
 
350
+ - 1.17.3 *Fixed bug with zero-length replacements.*
350
351
  - 1.17.2 *Added support for the enhanced local mirroring by updating [install-artifact-from-github](https://github.com/uhop/install-artifact-from-github).*
351
352
  - 1.17.1 *Fix for `lastIndex` for U+10000 - U+10FFFF UTF characters. Thx, [omg](https://github.com/omg).*
352
353
  - 1.17.0 *Updated GYP, added support for Node 17, updated deps.*
package/lib/replace.cc CHANGED
@@ -228,7 +228,9 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
228
228
  {
229
229
  size_t s = getUtf8CharSize(data[lastIndex]);
230
230
  lastIndex += s;
231
- if (s == 4 && n >= 2) --n; // this utf8 character will take two utf16 characters
231
+ if (s == 4 && n >= 2) {
232
+ --n; // this utf8 character will take two utf16 characters
233
+ }
232
234
  // the decrement above is protected to avoid an overflow of an unsigned integer
233
235
  }
234
236
  }
@@ -245,28 +247,30 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
245
247
  while (lastIndex <= size && re2->regexp.Match(str, lastIndex, size, anchor, &groups[0], groups.size()))
246
248
  {
247
249
  noMatch = false;
250
+ auto offset = match.data() - data;
248
251
  if (!re2->global && re2->sticky)
249
252
  {
250
- re2->lastIndex += replacee.isBuffer ? match.data() - data + match.size() - lastIndex : getUtf16Length(data + lastIndex, match.data() + match.size());
253
+ re2->lastIndex += replacee.isBuffer ? offset + match.size() - lastIndex : getUtf16Length(data + lastIndex, match.data() + match.size());
254
+ }
255
+ if (match.data() == data || offset > static_cast<long>(lastIndex))
256
+ {
257
+ result += std::string(data + lastIndex, offset - lastIndex);
251
258
  }
259
+ result += replace(replacer, replacer_size, groups, str, namedGroups);
252
260
  if (match.size())
253
261
  {
254
- if (match.data() == data || match.data() - data > static_cast<long>(lastIndex))
255
- {
256
- result += std::string(data + lastIndex, match.data() - data - lastIndex);
257
- }
258
- result += replace(replacer, replacer_size, groups, str, namedGroups);
259
- lastIndex = match.data() - data + match.size();
262
+ lastIndex = offset + match.size();
263
+ }
264
+ else if (offset < size)
265
+ {
266
+ auto sym_size = getUtf8CharSize(data[offset]);
267
+ result.append(data + offset, sym_size);
268
+ lastIndex = offset + sym_size;
260
269
  }
261
270
  else
262
271
  {
263
- result += replace(replacer, replacer_size, groups, str, namedGroups);
264
- size_t sym_size = getUtf8CharSize(data[lastIndex]);
265
- if (lastIndex < size)
266
- {
267
- result.append(data + lastIndex, sym_size);
268
- }
269
- lastIndex += sym_size;
272
+ lastIndex = size;
273
+ break;
270
274
  }
271
275
  if (!re2->global)
272
276
  {
@@ -295,7 +299,7 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
295
299
 
296
300
  inline Nan::Maybe<std::string> replace(const Nan::Callback *replacer, const std::vector<re2::StringPiece> &groups, const re2::StringPiece &str, const v8::Local<v8::Value> &input, bool useBuffers, const std::map<std::string, int> &namedGroups)
297
301
  {
298
- std::vector<v8::Local<v8::Value>> argv;
302
+ std::vector<v8::Local<v8::Value> > argv;
299
303
 
300
304
  auto context = Nan::GetCurrentContext();
301
305
 
@@ -377,7 +381,9 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
377
381
  {
378
382
  size_t s = getUtf8CharSize(data[lastIndex]);
379
383
  lastIndex += s;
380
- if (s == 4 && n >= 2) --n; // this utf8 character will take two utf16 characters
384
+ if (s == 4 && n >= 2) {
385
+ --n; // this utf8 character will take two utf16 characters
386
+ }
381
387
  // the decrement above is protected to avoid an overflow of an unsigned integer
382
388
  }
383
389
  }
@@ -396,38 +402,35 @@ static Nan::Maybe<std::string> replace(WrappedRE2 *re2, const StrVal &replacee,
396
402
  while (lastIndex <= size && re2->regexp.Match(str, lastIndex, size, anchor, &groups[0], groups.size()))
397
403
  {
398
404
  noMatch = false;
405
+ auto offset = match.data() - data;
399
406
  if (!re2->global && re2->sticky)
400
407
  {
401
- re2->lastIndex += replacee.isBuffer ? match.data() - data + match.size() - lastIndex : getUtf16Length(data + lastIndex, match.data() + match.size());
408
+ re2->lastIndex += replacee.isBuffer ? offset + match.size() - lastIndex : getUtf16Length(data + lastIndex, match.data() + match.size());
409
+ }
410
+ if (match.data() == data || offset > static_cast<long>(lastIndex))
411
+ {
412
+ result += std::string(data + lastIndex, offset - lastIndex);
402
413
  }
414
+ const auto part = replace(replacer, groups, str, input, useBuffers, namedGroups);
415
+ if (part.IsNothing())
416
+ {
417
+ return part;
418
+ }
419
+ result += part.FromJust();
403
420
  if (match.size())
404
421
  {
405
- if (match.data() == data || match.data() - data > static_cast<long>(lastIndex))
406
- {
407
- result += std::string(data + lastIndex, match.data() - data - lastIndex);
408
- }
409
- const auto part = replace(replacer, groups, str, input, useBuffers, namedGroups);
410
- if (part.IsNothing())
411
- {
412
- return part;
413
- }
414
- result += part.FromJust();
415
- lastIndex = match.data() - data + match.size();
422
+ lastIndex = offset + match.size();
423
+ }
424
+ else if (offset < size)
425
+ {
426
+ auto sym_size = getUtf8CharSize(data[offset]);
427
+ result.append(data + offset, sym_size);
428
+ lastIndex = offset + sym_size;
416
429
  }
417
430
  else
418
431
  {
419
- const auto part = replace(replacer, groups, str, input, useBuffers, namedGroups);
420
- if (part.IsNothing())
421
- {
422
- return part;
423
- }
424
- result += part.FromJust();
425
- size_t sym_size = getUtf8CharSize(data[lastIndex]);
426
- if (lastIndex < size)
427
- {
428
- result.append(data + lastIndex, sym_size);
429
- }
430
- lastIndex += sym_size;
432
+ lastIndex = size;
433
+ break;
431
434
  }
432
435
  if (!re2->global)
433
436
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "re2",
3
- "version": "1.17.2",
3
+ "version": "1.17.3",
4
4
  "description": "Bindings for RE2: fast, safe alternative to backtracking regular expression engines.",
5
5
  "homepage": "https://github.com/uhop/node-re2",
6
6
  "bugs": "https://github.com/uhop/node-re2/issues",
@@ -1,233 +1,284 @@
1
- "use strict";
2
-
3
-
4
- var unit = require("heya-unit");
5
- var RE2 = require("../re2");
1
+ 'use strict';
6
2
 
3
+ var unit = require('heya-unit');
4
+ var RE2 = require('../re2');
7
5
 
8
6
  // tests
9
7
 
10
8
  unit.add(module, [
11
-
12
- // These tests are copied from MDN:
13
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
14
-
15
- function test_replaceString(t) {
16
- "use strict";
17
-
18
- var re = new RE2(/apples/gi);
19
- var result = re.replace("Apples are round, and apples are juicy.", "oranges");
20
- eval(t.TEST("result === 'oranges are round, and oranges are juicy.'"));
21
-
22
- re = new RE2(/xmas/i);
23
- result = re.replace("Twas the night before Xmas...", "Christmas");
24
- eval(t.TEST("result === 'Twas the night before Christmas...'"));
25
-
26
- re = new RE2(/(\w+)\s(\w+)/);
27
- result = re.replace("John Smith", "$2, $1");
28
- eval(t.TEST("result === 'Smith, John'"));
29
- },
30
- function test_replaceFunReplacer(t) {
31
- "use strict";
32
-
33
- function replacer(match, p1, p2, p3, offset, string) {
34
- // p1 is nondigits, p2 digits, and p3 non-alphanumerics
35
- return [p1, p2, p3].join(' - ');
36
- }
37
-
38
- var re = new RE2(/([^\d]*)(\d*)([^\w]*)/);
39
- var result = re.replace("abc12345#$*%", replacer);
40
- eval(t.TEST("result === 'abc - 12345 - #$*%'"));
41
- },
42
- function test_replaceFunUpper(t) {
43
- "use strict";
44
-
45
- function upperToHyphenLower(match) {
46
- return '-' + match.toLowerCase();
47
- }
48
-
49
- var re = new RE2(/[A-Z]/g);
50
- var result = re.replace("borderTop", upperToHyphenLower);
51
- eval(t.TEST("result === 'border-top'"));
52
- },
53
- function test_replaceFunConvert(t) {
54
- "use strict";
55
-
56
- function convert(str, p1, offset, s) {
57
- return ((p1 - 32) * 5/9) + 'C';
58
- }
59
-
60
- var re = new RE2(/(\d+(?:\.\d*)?)F\b/g);
61
-
62
- eval(t.TEST("re.replace('32F', convert) === '0C'"));
63
- eval(t.TEST("re.replace('41F', convert) === '5C'"));
64
- eval(t.TEST("re.replace('50F', convert) === '10C'"));
65
- eval(t.TEST("re.replace('59F', convert) === '15C'"));
66
- eval(t.TEST("re.replace('68F', convert) === '20C'"));
67
- eval(t.TEST("re.replace('77F', convert) === '25C'"));
68
- eval(t.TEST("re.replace('86F', convert) === '30C'"));
69
- eval(t.TEST("re.replace('95F', convert) === '35C'"));
70
- eval(t.TEST("re.replace('104F', convert) === '40C'"));
71
- eval(t.TEST("re.replace('113F', convert) === '45C'"));
72
- eval(t.TEST("re.replace('212F', convert) === '100C'"));
73
- },
74
- {
75
- test: function test_replaceFunLoop(t) {
76
- "use strict";
77
-
78
- RE2(/(x_*)|(-)/g).replace("x-x_", function(match, p1, p2) {
79
- if (p1) { t.info("on: " + p1.length); }
80
- if (p2) { t.info("off: 1"); }
81
- });
82
- },
83
- logs: [
84
- {text: "on: 1"},
85
- {text: "off: 1"},
86
- {text: "on: 2"}
87
- ]
88
- },
89
- function test_replaceInvalid(t) {
90
- "use strict";
91
-
92
- var re = RE2('');
93
-
94
- try {
95
- re.replace({ toString() { throw "corner1"; } }, '');
96
- t.test(false); // shouldn't be here
97
- } catch(e) {
98
- eval(t.TEST("e === 'corner1'"));
99
- }
100
-
101
- try {
102
- re.replace('', { toString() { throw "corner2"; } });
103
- t.test(false); // shouldn't be here
104
- } catch(e) {
105
- eval(t.TEST("e === 'corner2'"));
106
- }
107
-
108
- var arg2Stringified = false;
109
-
110
- try {
111
- re.replace({ toString() { throw "corner1"; } }, { toString() { arg2Stringified = true; throw "corner2"; } });
112
- t.test(false); // shouldn't be here
113
- } catch(e) {
114
- eval(t.TEST("e === 'corner1'"));
115
- eval(t.TEST("!arg2Stringified"));
116
- }
117
-
118
- try {
119
- re.replace('', () => { throw "corner2"; });
120
- t.test(false); // shouldn't be here
121
- } catch(e) {
122
- eval(t.TEST("e === 'corner2'"));
123
- }
124
-
125
- try {
126
- re.replace('', () => ({ toString() { throw "corner2"; } }));
127
- t.test(false); // shouldn't be here
128
- } catch(e) {
129
- eval(t.TEST("e === 'corner2'"));
130
- }
131
- },
132
-
133
- // Unicode tests
134
-
135
- function test_replaceStrUnicode(t) {
136
- "use strict";
137
-
138
- var re = new RE2(/яблоки/gi);
139
- var result = re.replace("Яблоки красны, яблоки сочны.", "апельсины");
140
- eval(t.TEST("result === 'апельсины красны, апельсины сочны.'"));
141
-
142
- re = new RE2(/иван/i);
143
- result = re.replace("Могуч Иван Иванов...", "Сидор");
144
- eval(t.TEST("result === 'Могуч Сидор Иванов...'"));
145
-
146
- re = new RE2(/иван/ig);
147
- result = re.replace("Могуч Иван Иванов...", "Сидор");
148
- eval(t.TEST("result === 'Могуч Сидор Сидоров...'"));
149
-
150
- re = new RE2(/([а-яё]+)\s+([а-яё]+)/i);
151
- result = re.replace("Пётр Петров", "$2, $1");
152
- eval(t.TEST("result === 'Петров, Пётр'"));
153
- },
154
- function test_replaceFunUnicode(t) {
155
- "use strict";
156
-
157
- function replacer(match, offset, string) {
158
- t.test(typeof offset == "number");
159
- t.test(typeof string == "string");
160
- t.test(offset === 0 || offset === 7);
161
- t.test(string === "ИВАН и пЁтр");
162
- return match.charAt(0).toUpperCase() + match.substr(1).toLowerCase();
163
- }
164
-
165
- var re = new RE2(/(?:иван|пётр|сидор)/ig);
166
- var result = re.replace("ИВАН и пЁтр", replacer);
167
- eval(t.TEST("result === 'Иван и Пётр'"));
168
- },
169
-
170
- // Buffer tests
171
-
172
- function test_replaceStrBuffer(t) {
173
- "use strict";
174
-
175
- var re = new RE2(/яблоки/gi);
176
- var result = re.replace(new Buffer("Яблоки красны, яблоки сочны."), "апельсины");
177
- eval(t.TEST("result instanceof Buffer"));
178
- eval(t.TEST("result.toString() === 'апельсины красны, апельсины сочны.'"));
179
-
180
- result = re.replace(new Buffer("Яблоки красны, яблоки сочны."), new Buffer("апельсины"));
181
- eval(t.TEST("result instanceof Buffer"));
182
- eval(t.TEST("result.toString() === 'апельсины красны, апельсины сочны.'"));
183
-
184
- result = re.replace("Яблоки красны, яблоки сочны.", new Buffer("апельсины"));
185
- eval(t.TEST("typeof result == 'string'"));
186
- eval(t.TEST("result === 'апельсины красны, апельсины сочны.'"));
187
- },
188
- function test_replaceFunBuffer(t) {
189
- "use strict";
190
-
191
- function replacer(match, offset, string) {
192
- eval(t.TEST("match instanceof Buffer"));
193
- eval(t.TEST("typeof offset == 'number'"));
194
- eval(t.TEST("typeof string == 'string'"));
195
- eval(t.TEST("offset === 0 || offset === 12"));
196
- eval(t.TEST("string === 'ИВАН и пЁтр'"));
197
- var s = match.toString();
198
- return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();
199
- }
200
- replacer.useBuffers = true;
201
-
202
- var re = new RE2(/(?:иван|пётр|сидор)/ig);
203
- var result = re.replace("ИВАН и пЁтр", replacer);
204
- eval(t.TEST("typeof result == 'string'"));
205
- eval(t.TEST("result === 'Иван и Пётр'"));
206
- },
207
-
208
- // Sticky tests
209
-
210
- function test_replaceSticky(t) {
211
- "use strict";
212
-
213
- var re = new RE2(/[A-E]/y);
214
-
215
- eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === '!BCDEFABCDEF'"));
216
- eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'A!CDEFABCDEF'"));
217
- eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'AB!DEFABCDEF'"));
218
- eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'ABC!EFABCDEF'"));
219
- eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'ABCD!FABCDEF'"));
220
- eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'ABCDEFABCDEF'"));
221
- eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === '!BCDEFABCDEF'"));
222
-
223
- var re2 = new RE2(/[A-E]/gy);
224
-
225
- eval(t.TEST("re2.replace('ABCDEFABCDEF', '!') === '!!!!!FABCDEF'"));
226
- eval(t.TEST("re2.replace('FABCDEFABCDE', '!') === 'FABCDEFABCDE'"));
227
-
228
- re2.lastIndex = 3;
229
-
230
- eval(t.TEST("re2.replace('ABCDEFABCDEF', '!') === '!!!!!FABCDEF'"));
231
- eval(t.TEST("re2.lastIndex === 0"));
232
- }
9
+ // These tests are copied from MDN:
10
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
11
+
12
+ function test_replaceString(t) {
13
+ 'use strict';
14
+
15
+ var re = new RE2(/apples/gi);
16
+ var result = re.replace('Apples are round, and apples are juicy.', 'oranges');
17
+ eval(t.TEST("result === 'oranges are round, and oranges are juicy.'"));
18
+
19
+ re = new RE2(/xmas/i);
20
+ result = re.replace('Twas the night before Xmas...', 'Christmas');
21
+ eval(t.TEST("result === 'Twas the night before Christmas...'"));
22
+
23
+ re = new RE2(/(\w+)\s(\w+)/);
24
+ result = re.replace('John Smith', '$2, $1');
25
+ eval(t.TEST("result === 'Smith, John'"));
26
+ },
27
+ function test_replaceFunReplacer(t) {
28
+ 'use strict';
29
+
30
+ function replacer(match, p1, p2, p3, offset, string) {
31
+ // p1 is nondigits, p2 digits, and p3 non-alphanumerics
32
+ return [p1, p2, p3].join(' - ');
33
+ }
34
+
35
+ var re = new RE2(/([^\d]*)(\d*)([^\w]*)/);
36
+ var result = re.replace('abc12345#$*%', replacer);
37
+ eval(t.TEST("result === 'abc - 12345 - #$*%'"));
38
+ },
39
+ function test_replaceFunUpper(t) {
40
+ 'use strict';
41
+
42
+ function upperToHyphenLower(match) {
43
+ return '-' + match.toLowerCase();
44
+ }
45
+
46
+ var re = new RE2(/[A-Z]/g);
47
+ var result = re.replace('borderTop', upperToHyphenLower);
48
+ eval(t.TEST("result === 'border-top'"));
49
+ },
50
+ function test_replaceFunConvert(t) {
51
+ 'use strict';
52
+
53
+ function convert(str, p1, offset, s) {
54
+ return ((p1 - 32) * 5) / 9 + 'C';
55
+ }
56
+
57
+ var re = new RE2(/(\d+(?:\.\d*)?)F\b/g);
58
+
59
+ eval(t.TEST("re.replace('32F', convert) === '0C'"));
60
+ eval(t.TEST("re.replace('41F', convert) === '5C'"));
61
+ eval(t.TEST("re.replace('50F', convert) === '10C'"));
62
+ eval(t.TEST("re.replace('59F', convert) === '15C'"));
63
+ eval(t.TEST("re.replace('68F', convert) === '20C'"));
64
+ eval(t.TEST("re.replace('77F', convert) === '25C'"));
65
+ eval(t.TEST("re.replace('86F', convert) === '30C'"));
66
+ eval(t.TEST("re.replace('95F', convert) === '35C'"));
67
+ eval(t.TEST("re.replace('104F', convert) === '40C'"));
68
+ eval(t.TEST("re.replace('113F', convert) === '45C'"));
69
+ eval(t.TEST("re.replace('212F', convert) === '100C'"));
70
+ },
71
+ {
72
+ test: function test_replaceFunLoop(t) {
73
+ 'use strict';
74
+
75
+ RE2(/(x_*)|(-)/g).replace('x-x_', function (match, p1, p2) {
76
+ if (p1) {
77
+ t.info('on: ' + p1.length);
78
+ }
79
+ if (p2) {
80
+ t.info('off: 1');
81
+ }
82
+ });
83
+ },
84
+ logs: [{text: 'on: 1'}, {text: 'off: 1'}, {text: 'on: 2'}]
85
+ },
86
+ function test_replaceInvalid(t) {
87
+ 'use strict';
88
+
89
+ var re = RE2('');
90
+
91
+ try {
92
+ re.replace(
93
+ {
94
+ toString() {
95
+ throw 'corner1';
96
+ }
97
+ },
98
+ ''
99
+ );
100
+ t.test(false); // shouldn't be here
101
+ } catch (e) {
102
+ eval(t.TEST("e === 'corner1'"));
103
+ }
104
+
105
+ try {
106
+ re.replace('', {
107
+ toString() {
108
+ throw 'corner2';
109
+ }
110
+ });
111
+ t.test(false); // shouldn't be here
112
+ } catch (e) {
113
+ eval(t.TEST("e === 'corner2'"));
114
+ }
115
+
116
+ var arg2Stringified = false;
117
+
118
+ try {
119
+ re.replace(
120
+ {
121
+ toString() {
122
+ throw 'corner1';
123
+ }
124
+ },
125
+ {
126
+ toString() {
127
+ arg2Stringified = true;
128
+ throw 'corner2';
129
+ }
130
+ }
131
+ );
132
+ t.test(false); // shouldn't be here
133
+ } catch (e) {
134
+ eval(t.TEST("e === 'corner1'"));
135
+ eval(t.TEST('!arg2Stringified'));
136
+ }
137
+
138
+ try {
139
+ re.replace('', () => {
140
+ throw 'corner2';
141
+ });
142
+ t.test(false); // shouldn't be here
143
+ } catch (e) {
144
+ eval(t.TEST("e === 'corner2'"));
145
+ }
146
+
147
+ try {
148
+ re.replace('', () => ({
149
+ toString() {
150
+ throw 'corner2';
151
+ }
152
+ }));
153
+ t.test(false); // shouldn't be here
154
+ } catch (e) {
155
+ eval(t.TEST("e === 'corner2'"));
156
+ }
157
+ },
158
+
159
+ // Unicode tests
160
+
161
+ function test_replaceStrUnicode(t) {
162
+ 'use strict';
163
+
164
+ var re = new RE2(/яблоки/gi);
165
+ var result = re.replace('Яблоки красны, яблоки сочны.', 'апельсины');
166
+ eval(t.TEST("result === 'апельсины красны, апельсины сочны.'"));
167
+
168
+ re = new RE2(/иван/i);
169
+ result = re.replace('Могуч Иван Иванов...', 'Сидор');
170
+ eval(t.TEST("result === 'Могуч Сидор Иванов...'"));
171
+
172
+ re = new RE2(/иван/gi);
173
+ result = re.replace('Могуч Иван Иванов...', 'Сидор');
174
+ eval(t.TEST("result === 'Могуч Сидор Сидоров...'"));
175
+
176
+ re = new RE2(/([а-яё]+)\s+([а-яё]+)/i);
177
+ result = re.replace('Пётр Петров', '$2, $1');
178
+ eval(t.TEST("result === 'Петров, Пётр'"));
179
+ },
180
+ function test_replaceFunUnicode(t) {
181
+ 'use strict';
182
+
183
+ function replacer(match, offset, string) {
184
+ t.test(typeof offset == 'number');
185
+ t.test(typeof string == 'string');
186
+ t.test(offset === 0 || offset === 7);
187
+ t.test(string === 'ИВАН и пЁтр');
188
+ return match.charAt(0).toUpperCase() + match.substr(1).toLowerCase();
189
+ }
190
+
191
+ var re = new RE2(/(?:иван|пётр|сидор)/gi);
192
+ var result = re.replace('ИВАН и пЁтр', replacer);
193
+ eval(t.TEST("result === 'Иван и Пётр'"));
194
+ },
195
+
196
+ // Buffer tests
197
+
198
+ function test_replaceStrBuffer(t) {
199
+ 'use strict';
200
+
201
+ var re = new RE2(/яблоки/gi);
202
+ var result = re.replace(new Buffer('Яблоки красны, яблоки сочны.'), 'апельсины');
203
+ eval(t.TEST('result instanceof Buffer'));
204
+ eval(t.TEST("result.toString() === 'апельсины красны, апельсины сочны.'"));
205
+
206
+ result = re.replace(new Buffer('Яблоки красны, яблоки сочны.'), new Buffer('апельсины'));
207
+ eval(t.TEST('result instanceof Buffer'));
208
+ eval(t.TEST("result.toString() === 'апельсины красны, апельсины сочны.'"));
209
+
210
+ result = re.replace('Яблоки красны, яблоки сочны.', new Buffer('апельсины'));
211
+ eval(t.TEST("typeof result == 'string'"));
212
+ eval(t.TEST("result === 'апельсины красны, апельсины сочны.'"));
213
+ },
214
+ function test_replaceFunBuffer(t) {
215
+ 'use strict';
216
+
217
+ function replacer(match, offset, string) {
218
+ eval(t.TEST('match instanceof Buffer'));
219
+ eval(t.TEST("typeof offset == 'number'"));
220
+ eval(t.TEST("typeof string == 'string'"));
221
+ eval(t.TEST('offset === 0 || offset === 12'));
222
+ eval(t.TEST("string === 'ИВАН и пЁтр'"));
223
+ var s = match.toString();
224
+ return s.charAt(0).toUpperCase() + s.substr(1).toLowerCase();
225
+ }
226
+ replacer.useBuffers = true;
227
+
228
+ var re = new RE2(/(?:иван|пётр|сидор)/gi);
229
+ var result = re.replace('ИВАН и пЁтр', replacer);
230
+ eval(t.TEST("typeof result == 'string'"));
231
+ eval(t.TEST("result === 'Иван и Пётр'"));
232
+ },
233
+ function test_replace0(t) {
234
+ 'use strict';
235
+
236
+ function replacer(match) {
237
+ return 'MARKER' + match;
238
+ }
239
+
240
+ var re = new RE2(/^/g);
241
+ var result = re.replace('foo bar', 'MARKER');
242
+ eval(t.TEST("result === 'MARKERfoo bar'"));
243
+ result = re.replace('foo bar', replacer);
244
+ eval(t.TEST("result === 'MARKERfoo bar'"));
245
+
246
+ re = new RE2(/$/g);
247
+ result = re.replace('foo bar', 'MARKER');
248
+ eval(t.TEST("result === 'foo barMARKER'"));
249
+ result = re.replace('foo bar', replacer);
250
+ eval(t.TEST("result === 'foo barMARKER'"));
251
+
252
+ re = new RE2(/\b/g);
253
+ result = re.replace('foo bar', 'MARKER');
254
+ eval(t.TEST("result === 'MARKERfooMARKER MARKERbarMARKER'"));
255
+ result = re.replace('foo bar', replacer);
256
+ eval(t.TEST("result === 'MARKERfooMARKER MARKERbarMARKER'"));
257
+ },
258
+
259
+ // Sticky tests
260
+
261
+ function test_replaceSticky(t) {
262
+ 'use strict';
263
+
264
+ var re = new RE2(/[A-E]/y);
265
+
266
+ eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === '!BCDEFABCDEF'"));
267
+ eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'A!CDEFABCDEF'"));
268
+ eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'AB!DEFABCDEF'"));
269
+ eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'ABC!EFABCDEF'"));
270
+ eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'ABCD!FABCDEF'"));
271
+ eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === 'ABCDEFABCDEF'"));
272
+ eval(t.TEST("re.replace('ABCDEFABCDEF', '!') === '!BCDEFABCDEF'"));
273
+
274
+ var re2 = new RE2(/[A-E]/gy);
275
+
276
+ eval(t.TEST("re2.replace('ABCDEFABCDEF', '!') === '!!!!!FABCDEF'"));
277
+ eval(t.TEST("re2.replace('FABCDEFABCDE', '!') === 'FABCDEFABCDE'"));
278
+
279
+ re2.lastIndex = 3;
280
+
281
+ eval(t.TEST("re2.replace('ABCDEFABCDEF', '!') === '!!!!!FABCDEF'"));
282
+ eval(t.TEST('re2.lastIndex === 0'));
283
+ }
233
284
  ]);