Skip to content

Commit

Permalink
RegExp: add optimisation for RegExp that is simply checking if a stri…
Browse files Browse the repository at this point in the history
…ng ends with something

Speeds up .hash on Bangle.js
  • Loading branch information
gfwilliams committed Oct 29, 2024
1 parent dc6fe85 commit 59a9469
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 0 deletions.
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
E.nativeCall also checks to see if getting data as a flat string failed (could have caused segfault)
Fix for regression after #2547 fix (left hand operand of maths with a valueOf method)
X.on now allocates a new array for each new handler added, stops new handlers being called for the event that's currently being handled (#2559)
RegExp: add optimisation for RegExp that is simply checking if a string ends with something

2v24 : Bangle.js2: Add 'Bangle.touchRd()', 'Bangle.touchWr()'
Bangle.js2: After Bangle.showTestScreen, put Bangle.js into a hard off state (not soft off)
Expand Down
1 change: 1 addition & 0 deletions README_BuildProcess.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ There are some specifically that are useful for cutting a few bytes out of the b
* `ESPR_NO_SOFTWARE_I2C` - don't build in software I2C support
* `ESPR_NO_BLUETOOTH_MESSAGES` - don't include text versions of Bluetooth error messages (just the error number)
* `ESPR_LIMIT_DATE_RANGE` - limits the acceptable range for Date years (saves a few hundred bytes)
* `ESPR_NO_REGEX_OPTIMISE` - strips out some speed optimisations from the regex library

These are set automatically when `SAVE_ON_FLASH` is set (see `jsutils.h`)

Expand Down
47 changes: 47 additions & 0 deletions src/jswrap_regexp.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,35 @@ JsVar *jswrap_regexp_constructor(JsVar *str, JsVar *flags) {
jsvObjectSetChild(r, "flags", flags);
}
jsvObjectSetChildAndUnLock(r, "lastIndex", jsvNewFromInteger(0));
#ifndef ESPR_NO_REGEX_OPTIMISE
/* Quick shortcut - if we were using regex just to find the end of a string (eg just
normal chars and then $ at the end), do it with a single string compare which is faster.
We save `endsWith` here and then check in jswrap_regexp_exec */
JsvStringIterator it;
jsvStringIteratorNew(&it,str,0);
char ch = 0;
bool noControlChars = true;
char buf[32];
size_t bufc = 0;
while (jsvStringIteratorHasChar(&it)) {
bool wasSlash = ch=='\\';
if (ch && strchr(".[]()|^*+$",ch)) noControlChars = false;
ch = jsvStringIteratorGetCharAndNext(&it);
if (wasSlash) {
if (!strchr(".\\",ch)) {
noControlChars = false;
ch = 0;
}
}
if (ch && (wasSlash || ch!='\\') && bufc<sizeof(buf)) buf[bufc++]=ch;
if (wasSlash) ch = 0;
}
jsvStringIteratorFree(&it);
if (ch=='$' && noControlChars && bufc<sizeof(buf)) {
bufc--; // remove '$'
jsvObjectSetChildAndUnLock(r, "endsWith", jsvNewStringOfLength((unsigned int)bufc,buf));
}
#endif
return r;
}

Expand Down Expand Up @@ -359,6 +388,24 @@ Or with groups `/W(o)rld/.exec("Hello World")` returns:
JsVar *jswrap_regexp_exec(JsVar *parent, JsVar *arg) {
JsVar *str = jsvAsString(arg);
JsVarInt lastIndex = jsvObjectGetIntegerChild(parent, "lastIndex");
#ifndef ESPR_NO_REGEX_OPTIMISE
/* Quick shortcut - if we were using regex just to find the end of a string,
do it with a single string compare which is faster */
JsVar *endsWith = jsvObjectGetChildIfExists(parent, "endsWith");
if (endsWith) {
int idx = (int)jsvGetStringLength(arg) - (int)jsvGetStringLength(endsWith);
if ((lastIndex <= idx) && jsvCompareString(arg, endsWith, (size_t)idx,0,true)==0) {
JsVar *rmatch = jsvNewEmptyArray();
jsvSetArrayItem(rmatch, 0, endsWith);
jsvObjectSetChildAndUnLock(rmatch, "index", jsvNewFromInteger(idx));
jsvObjectSetChildAndUnLock(rmatch, "input", str);
jsvUnLock(endsWith);
return rmatch;
}
jsvUnLock(endsWith);
}
#endif
// Otherwise do proper regex
JsVar *regex = jsvObjectGetChildIfExists(parent, "source");
if (!jsvIsString(regex) || lastIndex>(JsVarInt)jsvGetStringLength(str)) {
jsvUnLock2(str,regex);
Expand Down

0 comments on commit 59a9469

Please sign in to comment.