Bug 820124, Part 2/2 - Handle fast removal in str_replace(). r=dvander

This commit is contained in:
Sean Stangl 2012-12-18 17:28:16 -08:00
parent d3ca2c8f04
commit c2faa9065b
2 changed files with 116 additions and 2 deletions

View File

@ -2308,6 +2308,114 @@ BuildDollarReplacement(JSContext *cx, JSString *textstrArg, JSLinearString *reps
return true;
}
struct StringRange
{
size_t start;
size_t length;
StringRange(size_t s, size_t l)
: start(s), length(l)
{ }
};
static JSString *
AppendSubstrings(JSContext *cx, Handle<JSStableString*> stableStr,
const StringRange *ranges, size_t rangesLen)
{
JS_ASSERT(rangesLen);
/* For single substrings, construct a dependent string. */
if (rangesLen == 1)
return js_NewDependentString(cx, stableStr, ranges[0].start, ranges[0].length);
/* Collect substrings into a rope. */
RopeBuilder rope(cx);
for (size_t i = 0; i < rangesLen; i++) {
const StringRange &sr = ranges[i];
RootedString substr(cx, js_NewDependentString(cx, stableStr, sr.start, sr.length));
if (!substr)
return NULL;
/* Appending to the rope permanently roots the substring. */
rope.append(substr);
}
return rope.result();
}
static bool
str_replace_regexp_remove(JSContext *cx, CallArgs args, HandleString str, RegExpShared &re)
{
Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx));
if (!stableStr)
return false;
Vector<StringRange, 16, SystemAllocPolicy> ranges;
StableCharPtr chars = stableStr->chars();
size_t charsLen = stableStr->length();
MatchPair match;
size_t startIndex = 0; /* Index used for iterating through the string. */
size_t lastIndex = 0; /* Index after last successful match. */
/* Accumulate StringRanges for unmatched substrings. */
while (startIndex <= charsLen) {
if (!JS_CHECK_OPERATION_LIMIT(cx))
return false;
RegExpRunStatus status = re.executeMatchOnly(cx, chars, charsLen, &startIndex, match);
if (status == RegExpRunStatus_Error)
return false;
if (status == RegExpRunStatus_Success_NotFound)
break;
/* Include the latest unmatched substring. */
if (size_t(match.start) > lastIndex) {
if (!ranges.append(StringRange(lastIndex, match.start - lastIndex)))
return false;
}
lastIndex = startIndex;
/* Non-global removal executes at most once. */
if (!re.global())
break;
if (match.isEmpty())
lastIndex++;
}
/* If unmatched, return the input string. */
if (!lastIndex) {
args.rval().setString(str);
return true;
}
/* The last successful match updates the RegExpStatics. */
cx->regExpStatics()->updateLazily(cx, stableStr, &re, lastIndex);
/* Include any remaining part of the string. */
if (lastIndex < charsLen) {
if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex)))
return false;
}
/* Handle the empty string before calling .begin(). */
if (ranges.empty()) {
args.rval().setString(cx->runtime->emptyString);
return true;
}
JSString *result = AppendSubstrings(cx, stableStr, ranges.begin(), ranges.length());
if (!result)
return false;
args.rval().setString(result);
return true;
}
static inline bool
str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata)
{
@ -2320,6 +2428,12 @@ str_replace_regexp(JSContext *cx, CallArgs args, ReplaceData &rdata)
RegExpStatics *res = cx->regExpStatics();
RegExpShared &re = rdata.g.regExp();
/* Optimize removal. */
if (rdata.repstr && rdata.repstr->length() == 0 && !rdata.dollar) {
JS_ASSERT(!rdata.lambda && !rdata.elembase);
return str_replace_regexp_remove(cx, args, rdata.str, re);
}
Value tmp;
if (!DoMatch(cx, res, rdata.str, re, ReplaceRegExpCallback, &rdata, REPLACE_ARGS, &tmp))
return false;

View File

@ -122,8 +122,8 @@ MatchPairs::initArray(size_t pairCount)
/* Initialize all MatchPair objects to invalid locations. */
for (size_t i = 0; i < pairCount; i++) {
pairs_[i].start = size_t(-1);
pairs_[i].limit = size_t(-1);
pairs_[i].start = -1;
pairs_[i].limit = -1;
}
return true;