Improve escapeLiteral performance of long strings by 30x by Llorx · Pull Request #3553 · brianc/node-postgres
if (backslash) { const pos = i % size; str[pos] = "\\"; } if (quote) { const pos = (i + 2) % size; str[pos] = "'"; }
stris a string (immutable), so this benchmark doesn’t actually test any quotes or backslashes.
Ah my bad. I was working with buffers and failed miserably when doing the benchmark haha. Here is the fixed benchmark:

Backslash penalizes way more than quote because of the callback to avoid another iteration to find a backslash. Still a great performance improvement, specially when there's nothing to clean (which is the usual case).
The code:
const { IsoBench } = require("iso-bench");
function escapeLiteralOriginal(str) {
let hasBackslash = false
let escaped = "'"
if (str == null) {
return "''"
}
if (typeof str !== 'string') {
return "''"
}
for (let i = 0; i < str.length; i++) {
const c = str[i]
if (c === "'") {
escaped += c + c
} else if (c === '\\') {
escaped += c + c
hasBackslash = true
} else {
escaped += c
}
}
escaped += "'"
if (hasBackslash === true) {
escaped = ' E' + escaped
}
return escaped
}
const escapeLiteralNew = function (str) {
if (typeof str !== 'string') {
return "''"
}
let hasBackslash = false
if (str.length < 13) {
let escaped = "'"
for (let i = 0; i < str.length; i++) {
const c = str[i]
if (c === "'") {
escaped += c + c
} else if (c === '\\') {
escaped += c + c
hasBackslash = true
} else {
escaped += c
}
}
escaped += "'"
if (hasBackslash === true) {
escaped = ' E' + escaped
}
return escaped
} else {
let escaped = str
.replace(/\\/g, () => {
hasBackslash = true
return '\\\\'
})
.replace(/'/g, "''")
if (hasBackslash) {
escaped = ` E'${escaped}'`
} else {
escaped = `'${escaped}'`
}
return escaped
}
}
function newArray(size, backslash, quote, count = 100) {
return new Array(count).fill(0).map((_, i) => {
let str = "a".repeat(size);
if (backslash) {
const pos = i % size;
str = `${str.substring(0, pos)}\\${str.substring(pos)}`;
}
if (quote) {
const pos = (i + 2) % size;
str = `${str.substring(0, pos)}'${str.substring(pos)}`;
}
return str;
});
}
new IsoBench()
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(10, false, false))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(10, false, false))
.endGroup("short string, backslash = false, quote = false")
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(10, true, false))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(10, true, false))
.endGroup("short string, backslash = true, quote = false")
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(10, false, true))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(10, false, true))
.endGroup("short string, backslash = false, quote = true")
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(10, true, true))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(10, true, true))
.endGroup("short string, backslash = true, quote = true")
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(1000, false, false))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(1000, false, false))
.endGroup("long string, backslash = false, quote = false")
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(1000, true, false))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(1000, true, false))
.endGroup("long string, backslash = true, quote = false")
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(1000, false, true))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(1000, false, true))
.endGroup("long string, backslash = false, quote = true")
.add("escapeLiteralNew", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralNew(arr[i]);
}
}, () => newArray(1000, true, true))
.add("escapeLiteralOriginal", (arr) => {
for (let i = 0; i < arr.length; i++) {
escapeLiteralOriginal(arr[i]);
}
}, () => newArray(1000, true, true))
.endGroup("long string, backslash = true, quote = true")
.consoleLog()
.run();