Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: src/string.js

Issue 13614: Improve speed of String.replace by around 33% by not constructing... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: Created 12 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. 1 // Copyright 2006-2008 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 return StringReplaceRegExp(subject, search, replace); 191 return StringReplaceRegExp(subject, search, replace);
192 } 192 }
193 } 193 }
194 194
195 // Convert the search argument to a string and search for it. 195 // Convert the search argument to a string and search for it.
196 search = ToString(search); 196 search = ToString(search);
197 var start = %StringIndexOf(subject, search, 0); 197 var start = %StringIndexOf(subject, search, 0);
198 if (start < 0) return subject; 198 if (start < 0) return subject;
199 var end = start + search.length; 199 var end = start + search.length;
200 200
201 var builder = new StringBuilder(); 201 var builder = new ReplaceResultBuilder(subject);
202 // prefix 202 // prefix
203 builder.add(SubString(subject, 0, start)); 203 builder.addSpecialSlice(0, start);
204 204
205 // Compute the string to replace with. 205 // Compute the string to replace with.
206 if (IS_FUNCTION(replace)) { 206 if (IS_FUNCTION(replace)) {
207 builder.add(replace.call(null, search, start, subject)); 207 builder.add(replace.call(null, search, start, subject));
208 } else { 208 } else {
209 ExpandReplacement(ToString(replace), subject, [ start, end ], builder); 209 ExpandReplacement(ToString(replace), subject, [ start, end ], builder);
210 } 210 }
211 211
212 // suffix 212 // suffix
213 builder.add(SubString(subject, end, subject.length)); 213 builder.addSpecialSlice(end, subject.length);
214 214
215 return builder.generate(); 215 return builder.generate();
216 } 216 }
217 217
218 218
219 // Helper function for regular expressions in String.prototype.replace. 219 // Helper function for regular expressions in String.prototype.replace.
220 function StringReplaceRegExp(subject, regexp, replace) { 220 function StringReplaceRegExp(subject, regexp, replace) {
221 // Compute an array of matches; each match is really a list of 221 // Compute an array of matches; each match is really a list of
222 // captures - pairs of (start, end) indexes into the subject string. 222 // captures - pairs of (start, end) indexes into the subject string.
223 var matches; 223 var matches;
224 if (regexp.global) { 224 if (regexp.global) {
225 matches = DoRegExpExecGlobal(regexp, subject); 225 matches = DoRegExpExecGlobal(regexp, subject);
226 if (matches.length == 0) return subject; 226 if (matches.length == 0) return subject;
227 } else { 227 } else {
228 var captures = DoRegExpExec(regexp, subject, 0); 228 var captures = DoRegExpExec(regexp, subject, 0);
229 if (IS_NULL(captures)) return subject; 229 if (IS_NULL(captures)) return subject;
230 matches = [ captures ]; 230 matches = [ captures ];
231 } 231 }
232 232
233 // Determine the number of matches. 233 // Determine the number of matches.
234 var length = matches.length; 234 var length = matches.length;
235 235
236 // Build the resulting string of subject slices and replacements. 236 // Build the resulting string of subject slices and replacements.
237 var result = new StringBuilder(); 237 var result = new ReplaceResultBuilder(subject);
238 var previous = 0; 238 var previous = 0;
239 // The caller of StringReplaceRegExp must ensure that replace is not a 239 // The caller of StringReplaceRegExp must ensure that replace is not a
240 // function. 240 // function.
241 replace = ToString(replace); 241 replace = ToString(replace);
242 for (var i = 0; i < length; i++) { 242 if (%StringIndexOf(replace, "$", 0) < 0) {
243 var captures = matches[i]; 243 for (var i = 0; i < length; i++) {
244 result.add(SubString(subject, previous, captures[0])); 244 var captures = matches[i];
245 ExpandReplacement(replace, subject, captures, result); 245 result.addSpecialSlice(previous, captures[0]);
246 previous = captures[1]; // continue after match 246 result.add(replace);
247 previous = captures[1]; // continue after match
248 }
249 } else {
250 for (var i = 0; i < length; i++) {
251 var captures = matches[i];
252 result.addSpecialSlice(previous, captures[0]);
253 ExpandReplacement(replace, subject, captures, result);
254 previous = captures[1]; // continue after match
255 }
247 } 256 }
248 result.add(SubString(subject, previous, subject.length)); 257 result.addSpecialSlice(previous, subject.length);
249 return result.generate(); 258 return result.generate();
250 }; 259 };
251 260
252 261
253 // Expand the $-expressions in the string and return a new string with 262 // Expand the $-expressions in the string and return a new string with
254 // the result. 263 // the result.
255 function ExpandReplacement(string, subject, captures, builder) { 264 function ExpandReplacement(string, subject, captures, builder) {
256 var next = %StringIndexOf(string, '$', 0); 265 var next = %StringIndexOf(string, '$', 0);
257 if (next < 0) { 266 if (next < 0) {
258 builder.add(string); 267 builder.add(string);
259 return; 268 return;
260 } 269 }
261 270
262 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102. 271 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
263 var m = captures.length >> 1; // includes the match 272 var m = captures.length >> 1; // includes the match
264 273
265 if (next > 0) builder.add(SubString(string, 0, next)); 274 if (next > 0) builder.add(SubString(string, 0, next));
266 var length = string.length; 275 var length = string.length;
267 276
268 while (true) { 277 while (true) {
269 var expansion = '$'; 278 var expansion = '$';
270 var position = next + 1; 279 var position = next + 1;
271 if (position < length) { 280 if (position < length) {
272 var peek = %StringCharCodeAt(string, position); 281 var peek = %StringCharCodeAt(string, position);
273 if (peek == 36) { // $$ 282 if (peek == 36) { // $$
274 ++position; 283 ++position;
284 builder.add('$');
275 } else if (peek == 38) { // $& - match 285 } else if (peek == 38) { // $& - match
276 ++position; 286 ++position;
277 expansion = SubString(subject, captures[0], captures[1]); 287 builder.addSpecialSlice(captures[0], captures[1]);
278 } else if (peek == 96) { // $` - prefix 288 } else if (peek == 96) { // $` - prefix
279 ++position; 289 ++position;
280 expansion = SubString(subject, 0, captures[0]); 290 builder.addSpecialSlice(0, captures[0]);
281 } else if (peek == 39) { // $' - suffix 291 } else if (peek == 39) { // $' - suffix
282 ++position; 292 ++position;
283 expansion = SubString(subject, captures[1], subject.length); 293 builder.addSpecialSlice(captures[1], subject.length);
284 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9 294 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
285 ++position; 295 ++position;
286 var n = peek - 48; 296 var n = peek - 48;
287 if (position < length) { 297 if (position < length) {
288 peek = %StringCharCodeAt(string, position); 298 peek = %StringCharCodeAt(string, position);
289 // $nn, 01 <= nn <= 99 299 // $nn, 01 <= nn <= 99
290 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) { 300 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
291 var nn = n * 10 + (peek - 48); 301 var nn = n * 10 + (peek - 48);
292 if (nn < m) { 302 if (nn < m) {
293 // If the two digit capture reference is within range of 303 // If the two digit capture reference is within range of
294 // the captures, we use it instead of the single digit 304 // the captures, we use it instead of the single digit
295 // one. Otherwise, we fall back to using the single 305 // one. Otherwise, we fall back to using the single
296 // digit reference. This matches the behavior of 306 // digit reference. This matches the behavior of
297 // SpiderMonkey. 307 // SpiderMonkey.
298 ++position; 308 ++position;
299 n = nn; 309 n = nn;
300 } 310 }
301 } 311 }
302 } 312 }
303 if (0 < n && n < m) { 313 if (0 < n && n < m) {
304 expansion = CaptureString(subject, captures, n); 314 addCaptureString(builder, captures, n);
305 if (IS_UNDEFINED(expansion)) expansion = "";
306 } else { 315 } else {
307 // Because of the captures range check in the parsing of two 316 // Because of the captures range check in the parsing of two
308 // digit capture references, we can only enter here when a 317 // digit capture references, we can only enter here when a
309 // single digit capture reference is outside the range of 318 // single digit capture reference is outside the range of
310 // captures. 319 // captures.
320 builder.add('$');
311 --position; 321 --position;
312 } 322 }
313 } 323 }
324 } else {
325 builder.add('$');
314 } 326 }
315 327
316 // Append the $ expansion and go the the next $ in the string. 328 // Go the the next $ in the string.
317 builder.add(expansion);
318 next = %StringIndexOf(string, '$', position); 329 next = %StringIndexOf(string, '$', position);
319 330
320 // Return if there are no more $ characters in the string. If we 331 // Return if there are no more $ characters in the string. If we
321 // haven't reached the end, we need to append the suffix. 332 // haven't reached the end, we need to append the suffix.
322 if (next < 0) { 333 if (next < 0) {
323 if (position < length) { 334 if (position < length) {
324 builder.add(SubString(string, position, length)); 335 builder.add(SubString(string, position, length));
325 } 336 }
326 return; 337 return;
327 } 338 }
(...skipping 10 matching lines...) Expand all
338 var scaled = index << 1; 349 var scaled = index << 1;
339 // Compute start and end. 350 // Compute start and end.
340 var start = captures[scaled]; 351 var start = captures[scaled];
341 var end = captures[scaled + 1]; 352 var end = captures[scaled + 1];
342 // If either start or end is missing return undefined. 353 // If either start or end is missing return undefined.
343 if (start < 0 || end < 0) return; 354 if (start < 0 || end < 0) return;
344 return SubString(string, start, end); 355 return SubString(string, start, end);
345 }; 356 };
346 357
347 358
359 // Add the string of a given PCRE capture to the ReplaceResultBuilder
360 function addCaptureString(builder, captures, index) {
361 // Scale the index.
362 var scaled = index << 1;
363 // Compute start and end.
364 var start = captures[scaled];
365 var end = captures[scaled + 1];
366 // If either start or end is missing return.
367 if (start < 0 || end < 0) return;
368 builder.addSpecialSlice(start, end);
369 };
370
371
348 // Helper function for replacing regular expressions with the result of a 372 // Helper function for replacing regular expressions with the result of a
349 // function application in String.prototype.replace. The function application 373 // function application in String.prototype.replace. The function application
350 // must be interleaved with the regexp matching (contrary to ECMA-262 374 // must be interleaved with the regexp matching (contrary to ECMA-262
351 // 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses 375 // 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses
352 // the static properties of the RegExp constructor. Example: 376 // the static properties of the RegExp constructor. Example:
353 // 'abcd'.replace(/(.)/g, function() { return RegExp.$1; } 377 // 'abcd'.replace(/(.)/g, function() { return RegExp.$1; }
354 // should be 'abcd' and not 'dddd' (or anything else). 378 // should be 'abcd' and not 'dddd' (or anything else).
355 function StringReplaceRegExpWithFunction(subject, regexp, replace) { 379 function StringReplaceRegExpWithFunction(subject, regexp, replace) {
356 var result = new ReplaceResultBuilder(subject); 380 var result = new ReplaceResultBuilder(subject);
357 // Captures is an array of pairs of (start, end) indices for the match and 381 // Captures is an array of pairs of (start, end) indices for the match and
(...skipping 467 matching lines...) Expand 10 before | Expand all | Expand 10 after
825 "italics", StringItalics, 849 "italics", StringItalics,
826 "small", StringSmall, 850 "small", StringSmall,
827 "strike", StringStrike, 851 "strike", StringStrike,
828 "sub", StringSub, 852 "sub", StringSub,
829 "sup", StringSup 853 "sup", StringSup
830 )); 854 ));
831 } 855 }
832 856
833 857
834 SetupString(); 858 SetupString();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698