Skip to content

Commit 405da62

Browse files
committed
perf: improve parse performance by 12%
1 parent 0aba996 commit 405da62

File tree

2 files changed

+29
-31
lines changed

2 files changed

+29
-31
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,17 @@ console.log(qs.stringify({ foo: ['bar', 'baz'] }))
4848
╔═════════════════════════════════════════╤═════════╤═══════════════════╤═══════════╗
4949
║ Slower tests │ Samples │ Result │ Tolerance ║
5050
╟─────────────────────────────────────────┼─────────┼───────────────────┼───────────╢
51-
║ qs │ 10000 │ 317841.53 op/sec │ ± 1.18 % ║
52-
║ query-string │ 10000 341009.91 op/sec │ ± 1.06 % ║
53-
║ querystringify │ 1000 451609.79 op/sec │ ± 0.61 % ║
54-
║ @aws-sdk/querystring-parser │ 1000477241.09 op/sec │ ± 0.69 % ║
55-
║ URLSearchParams-with-Object.fromEntries │ 10000 870095.07 op/sec │ ± 3.29 % ║
56-
║ URLSearchParams-with-construct │ 10000 │ 1232650.60 op/sec │ ± 2.95 % ║
57-
║ node:querystring │ 10000 │ 1462802.04 op/sec │ ± 3.69 % ║
51+
║ qs │ 10000 │ 290743.91 op/sec │ ± 1.87 % ║
52+
║ query-string │ 1500 333025.25 op/sec │ ± 0.98 % ║
53+
║ querystringify │ 10000 430382.40 op/sec │ ± 1.95 % ║
54+
║ @aws-sdk/querystring-parser │ 3000452331.29 op/sec │ ± 0.87 % ║
55+
║ URLSearchParams-with-Object.fromEntries │ 2000 862635.26 op/sec │ ± 0.87 % ║
56+
║ URLSearchParams-with-construct │ 10000 │ 1216331.19 op/sec │ ± 3.19 % ║
57+
║ node:querystring │ 10000 │ 1453849.93 op/sec │ ± 4.50 % ║
5858
╟─────────────────────────────────────────┼─────────┼───────────────────┼───────────╢
5959
║ Fastest test │ Samples │ Result │ Tolerance ║
6060
╟─────────────────────────────────────────┼─────────┼───────────────────┼───────────╢
61-
║ fast-querystring │ 10000 │ 1809816.84 op/sec │ ± 3.19 % ║
61+
║ fast-querystring │ 10000 │ 2047629.50 op/sec │ ± 3.72 % ║
6262
╚═════════════════════════════════════════╧═════════╧═══════════════════╧═══════════╝
6363
```
6464

lib/parse.js

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
"use strict";
2-
31
const fastDecode = require("fast-decode-uri-component");
42

53
const plusRegex = /\+/g;
4+
const Empty = function () {};
5+
Empty.prototype = Object.create(null);
66

77
/**
88
* @callback parse
99
* @param {string} input
1010
*/
1111
function parse(input) {
12-
const result = Object.create(null);
12+
// Optimization: Use new Empty() instead of Object.create(null) for performance
13+
// v8 has a better optimization for initializing functions compared to Object
14+
const result = new Empty();
1315

1416
if (typeof input !== "string") {
1517
return result;
1618
}
1719

18-
const inputLength = input.length;
20+
let inputLength = input.length;
1921
let key = "";
2022
let value = "";
2123
let startingIndex = -1;
@@ -37,13 +39,8 @@ function parse(input) {
3739
keyEndingIndex = hasBothKeyValuePair ? equalityIndex : i;
3840
key = input.slice(startingIndex + 1, keyEndingIndex);
3941

40-
// Only update value if '=' exists in the current range
41-
if (hasBothKeyValuePair) {
42-
value = input.slice(equalityIndex + 1, i);
43-
}
44-
4542
// Add key/value pair only if the range size is greater than 1; a.k.a. contains at least "="
46-
if (hasBothKeyValuePair || i - startingIndex > 1) {
43+
if (hasBothKeyValuePair || key.length > 0) {
4744
// Optimization: Replace '+' with space
4845
if (keyHasPlus) {
4946
key = key.replace(plusRegex, " ");
@@ -55,6 +52,8 @@ function parse(input) {
5552
}
5653

5754
if (hasBothKeyValuePair) {
55+
value = input.slice(equalityIndex + 1, i);
56+
5857
if (valueHasPlus) {
5958
value = value.replace(plusRegex, " ");
6059
}
@@ -63,12 +62,11 @@ function parse(input) {
6362
value = fastDecode(value) || value;
6463
}
6564
}
65+
const currentValue = result[key];
6666

67-
if (result[key] === undefined) {
67+
if (currentValue === undefined) {
6868
result[key] = value;
6969
} else {
70-
const currentValue = result[key];
71-
7270
// Optimization: value.pop is faster than Array.isArray(value)
7371
if (currentValue.pop) {
7472
currentValue.push(value);
@@ -86,31 +84,31 @@ function parse(input) {
8684
shouldDecodeValue = false;
8785
keyHasPlus = false;
8886
valueHasPlus = false;
89-
hasBothKeyValuePair = false;
9087
}
9188
// Check '='
9289
else if (c === 61) {
90+
if (equalityIndex <= startingIndex) {
91+
equalityIndex = i;
92+
}
9393
// If '=' character occurs again, we should decode the input.
94-
if (equalityIndex > startingIndex) {
94+
else {
9595
shouldDecodeValue = true;
96-
} else {
97-
equalityIndex = i;
9896
}
9997
}
10098
// Check '+', and remember to replace it with empty space.
10199
else if (c === 43) {
102-
if (equalityIndex <= startingIndex) {
103-
keyHasPlus = true;
104-
} else {
100+
if (equalityIndex > startingIndex) {
105101
valueHasPlus = true;
102+
} else {
103+
keyHasPlus = true;
106104
}
107105
}
108106
// Check '%' character for encoding
109107
else if (c === 37) {
110-
if (equalityIndex <= startingIndex) {
111-
shouldDecodeKey = true;
112-
} else {
108+
if (equalityIndex > startingIndex) {
113109
shouldDecodeValue = true;
110+
} else {
111+
shouldDecodeKey = true;
114112
}
115113
}
116114
}

0 commit comments

Comments
 (0)