From 034b4e552097cd42e89c0daf2c4f3a6aa4625a4e Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Tue, 10 Sep 2024 00:20:27 -0700 Subject: [PATCH] perf: separate no-comment minifier for better performance --- package.json | 1 + src/native/lib.d | 67 ++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 0c3b531..e5a2173 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "format.d": "dub run --build=release --quiet dfmt -- --soft_max_line_length 110 --indent_size 2 --inplace ./src ./benchmark", "lint": "eslint . --fix", "prepublishOnly": "shx rm -rf ./dist/tsconfig.tsbuildinfo ./dist/*.zip ./dist/build.* && chmod +x ./dist/*/minijson", + "start.benchmark.js": "node ./benchmark/js-benchmark.mjs", "start.benchmark.node": "node ./benchmark/native-benchmark.mjs", "start.browser": "servor ./dist/ --browse --reload", "start.node": "node ./dist/node/cli.js", diff --git a/src/native/lib.d b/src/native/lib.d index 5f869a0..2b0939c 100644 --- a/src/native/lib.d +++ b/src/native/lib.d @@ -18,6 +18,60 @@ const tokenizerNoComment = ctRegex!(`[\n\r"[]]`, "g"); the minified json string */ string minifyString(in string jsonString, in bool hasComment = false) @trusted +{ + return hasComment ? minifyStringWithComments(jsonString) : minifyStringNoComments(jsonString); +} + +string minifyStringNoComments(in string jsonString) @trusted +{ + auto in_string = false; + string result; + size_t from = 0; + auto rightContext = ""; + + auto match = jsonString.matchAll(tokenizerNoComment); + + while (!match.empty()) + { + const matchFrontHit = match.front().hit(); + + rightContext = match.post(); + + // update from for the next iteration + const prevFrom = from; + from = jsonString.length - rightContext.length; // lastIndex + + auto leftContextSubstr = match.pre()[prevFrom .. $]; + const noLeftContext = leftContextSubstr.length == 0; + if (!noLeftContext) + { + if (!in_string) + { + leftContextSubstr = remove_spaces(leftContextSubstr); + } + result ~= leftContextSubstr; + } + if (matchFrontHit == "\"") + { + if (!in_string || noLeftContext || hasNoSlashOrEvenNumberOfSlashes(leftContextSubstr)) + { + // start of string with ", or unescaped " character found to end string + in_string = !in_string; + } + --from; // include " character in next catch + rightContext = jsonString[from .. $]; + } + else if (notSlashAndNoSpaceOrBreak(matchFrontHit)) + { + result ~= matchFrontHit; + } + match.popFront(); + } + result ~= rightContext; + return result; +} + +string minifyStringWithComments(in string jsonString) @trusted { auto in_string = false; auto in_multiline_comment = false; @@ -26,9 +80,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted size_t from = 0; auto rightContext = ""; - const tokenizer = !hasComment ? tokenizerNoComment : tokenizerWithComment; - - auto match = jsonString.matchAll(tokenizer); + auto match = jsonString.matchAll(tokenizerWithComment); while (!match.empty()) { @@ -40,10 +92,9 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted const prevFrom = from; from = jsonString.length - rightContext.length; // lastIndex - const notInComment = (!in_multiline_comment && !in_singleline_comment); - const noCommentOrNotInComment = !hasComment || notInComment; + const notInComment = !in_multiline_comment && !in_singleline_comment; - if (noCommentOrNotInComment) + if (notInComment) { auto leftContextSubstr = match.pre()[prevFrom .. $]; const noLeftContext = leftContextSubstr.length == 0; @@ -67,7 +118,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted } } // comments - if (hasComment && !in_string) + if (!in_string) { if (notInComment) { @@ -93,7 +144,7 @@ string minifyString(in string jsonString, in bool hasComment = false) @trusted in_singleline_comment = false; } } - else if (!hasComment && notSlashAndNoSpaceOrBreak(matchFrontHit)) + else if (notSlashAndNoSpaceOrBreak(matchFrontHit)) { result ~= matchFrontHit; }