Skip to content

perf(json): avoid first-char regex for direct JSON bodies#744

Open
Phillip9587 wants to merge 1 commit into
expressjs:masterfrom
Phillip9587:improve-json-strict-mode
Open

perf(json): avoid first-char regex for direct JSON bodies#744
Phillip9587 wants to merge 1 commit into
expressjs:masterfrom
Phillip9587:improve-json-strict-mode

Conversation

@Phillip9587

Copy link
Copy Markdown
Member

Skip the RFC 7159 whitespace scan when strict JSON bodies start directly with { or [. This avoids regex execution on the common valid path while preserving strict-mode handling for leading whitespace and primitive values.

Updated the first non-whitespace regex to match end-of-string so whitespace-only bodies return undefined without relying on a failed match/backtracking path.

Skip the RFC 7159 whitespace scan when strict JSON bodies start directly with `{` or `[`. This avoids regex execution on the common valid path while preserving strict-mode handling for leading whitespace and primitive values.

Update the first non-whitespace regex to match end-of-string so whitespace-only bodies return `undefined` without relying on a failed match/backtracking path.
@bjohansebas

Copy link
Copy Markdown
Member

Could you tell us how much of an improvement there is?

@Phillip9587

Copy link
Copy Markdown
Member Author

Micro Benchmark shows:

case test json iterations old median ns new median ns delta speedup
direct object "{"hello":"world","n":123,"ok":true}" 2000000 239.586 210.294 -12.23% 1.139x
direct array "[1,2,3,4,5,6,7,8,9,10]" 2000000 168.300 128.147 -23.86% 1.313x
leading whitespace object " \n\t\r {"hello":"world","n":123,"ok":true}" 1000000 254.757 269.237 +5.68% 0.946x
strict violation primitive "true" 200000 9609.080 9533.889 -0.78% 1.008x
whitespace only " \n\t\r" 200000 9138.759 9127.327 -0.13% 1.001x

The load test from perf-wg do not show a big difference because http-handling, streaming and json parsing take much longer.

Comment thread lib/types/json.js
* %x0D ) ; Carriage return
*/
const FIRST_CHAR_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d])/ // eslint-disable-line no-control-regex
const FIRST_NON_WHITESPACE_REGEXP = /^[\x20\x09\x0a\x0d]*([^\x20\x09\x0a\x0d]|$)/ // eslint-disable-line no-control-regex

@bjohansebas bjohansebas Jul 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, for future reviewers, here's the regex change.

" {json}"
master=match[1]="{"
PR=match[1]="{"
"123" (primitivo)
master=match[1]="1"
PR=match[1]="1"
" " (solo whitespace)
master=NULL (no match)
PR=match[1]=""
"" (vacio)
master=NULL (no match)
PR=match[1]=""

@bjohansebas bjohansebas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants