diff options
| author | Mattias EngdegÄrd | 2025-09-04 11:22:57 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2025-09-07 14:39:24 +0200 |
| commit | 36c8ebe78a048db7886f070168858d457b486caf (patch) | |
| tree | fa4bc9f4a368b4e61915fa76c974567ceb02d9c2 | |
| parent | 11b3af64251a899c697b685cf7b311274be8dc6d (diff) | |
| download | emacs-36c8ebe78a048db7886f070168858d457b486caf.tar.gz emacs-36c8ebe78a048db7886f070168858d457b486caf.zip | |
Avoid unnecessary text decoding in jsonrpc for speed (bug#79361)
The built-in JSON parser works on undecoded (unibyte) input; decoding
received data is just a waste and does take time even when all-ASCII.
* lisp/jsonrpc.el (initialize-instance): Use unibyte process buffer and
binary coding for process I/O, implying unibyte strings being passed to
the filter function.
(jsonrpc-connection-send): More efficient message generation.
(jsonrpc--json-read): Compatibility code for the old elisp json parser.
(jsonrpc--process-filter): Faster header-matching regexp.
| -rw-r--r-- | lisp/jsonrpc.el | 34 |
1 files changed, 21 insertions, 13 deletions
diff --git a/lisp/jsonrpc.el b/lisp/jsonrpc.el index 6c969120926..bb75196cdc8 100644 --- a/lisp/jsonrpc.el +++ b/lisp/jsonrpc.el | |||
| @@ -548,7 +548,9 @@ connection object, called when the process dies.") | |||
| 548 | (set-process-buffer proc (get-buffer-create (format " *%s output*" name))) | 548 | (set-process-buffer proc (get-buffer-create (format " *%s output*" name))) |
| 549 | (set-process-filter proc #'jsonrpc--process-filter) | 549 | (set-process-filter proc #'jsonrpc--process-filter) |
| 550 | (set-process-sentinel proc #'jsonrpc--process-sentinel) | 550 | (set-process-sentinel proc #'jsonrpc--process-sentinel) |
| 551 | (set-process-coding-system proc 'binary 'binary) | ||
| 551 | (with-current-buffer (process-buffer proc) | 552 | (with-current-buffer (process-buffer proc) |
| 553 | (set-buffer-multibyte nil) | ||
| 552 | (buffer-disable-undo) | 554 | (buffer-disable-undo) |
| 553 | (set-marker (process-mark proc) (point-min)) | 555 | (set-marker (process-mark proc) (point-min)) |
| 554 | (let ((inhibit-read-only t)) | 556 | (let ((inhibit-read-only t)) |
| @@ -578,16 +580,11 @@ connection object, called when the process dies.") | |||
| 578 | (id 'request) | 580 | (id 'request) |
| 579 | (method 'notification))) | 581 | (method 'notification))) |
| 580 | (converted (jsonrpc-convert-to-endpoint connection args kind)) | 582 | (converted (jsonrpc-convert-to-endpoint connection args kind)) |
| 581 | (json (jsonrpc--json-encode converted)) | 583 | (json (jsonrpc--json-encode converted))) |
| 582 | (headers | ||
| 583 | `(("Content-Length" . ,(format "%d" (string-bytes json))) | ||
| 584 | ;; ("Content-Type" . "application/vscode-jsonrpc; charset=utf-8") | ||
| 585 | ))) | ||
| 586 | (process-send-string | 584 | (process-send-string |
| 587 | (jsonrpc--process connection) | 585 | (jsonrpc--process connection) |
| 588 | (cl-loop for (header . value) in headers | 586 | (concat "Content-Length: " (number-to-string (string-bytes json)) "\r\n" |
| 589 | concat (concat header ": " value "\r\n") into header-section | 587 | "\r\n" json)) |
| 590 | finally return (format "%s\r\n%s" header-section json))) | ||
| 591 | (jsonrpc--event | 588 | (jsonrpc--event |
| 592 | connection | 589 | connection |
| 593 | 'client | 590 | 'client |
| @@ -641,11 +638,19 @@ and delete the network process." | |||
| 641 | :false-object :json-false)) | 638 | :false-object :json-false)) |
| 642 | (require 'json) | 639 | (require 'json) |
| 643 | (defvar json-object-type) | 640 | (defvar json-object-type) |
| 644 | (declare-function json-read "json" ()) | 641 | (declare-function json-read-from-string "json" (string)) |
| 645 | (lambda () | 642 | (lambda () |
| 646 | (let ((json-object-type 'plist)) | 643 | (let ((json-object-type 'plist)) |
| 647 | (json-read)))) | 644 | ;; `json-read' can't be used because the old json API requires |
| 648 | "Read JSON object in buffer, move point to end of buffer.") | 645 | ;; decoded input. |
| 646 | (prog1 | ||
| 647 | (json-read-from-string | ||
| 648 | (decode-coding-string | ||
| 649 | (buffer-substring-no-properties (point) (point-max)) | ||
| 650 | 'utf-8-unix t)) | ||
| 651 | (goto-char (point-max)))))) | ||
| 652 | "Read JSON object in (binary unibyte) buffer from point. | ||
| 653 | Move point to end of buffer.") | ||
| 649 | 654 | ||
| 650 | (defalias 'jsonrpc--json-encode | 655 | (defalias 'jsonrpc--json-encode |
| 651 | (if (fboundp 'json-serialize) | 656 | (if (fboundp 'json-serialize) |
| @@ -745,8 +750,11 @@ and delete the network process." | |||
| 745 | ;; | 750 | ;; |
| 746 | (setq expected-bytes | 751 | (setq expected-bytes |
| 747 | (and (search-forward-regexp | 752 | (and (search-forward-regexp |
| 748 | "\\(?:.*: .*\r\n\\)*Content-Length: \ | 753 | (rx bol "Content-Length: " (group (+ digit)) |
| 749 | *\\([[:digit:]]+\\)\r\n\\(?:.*: .*\r\n\\)*\r\n" | 754 | "\r\n" |
| 755 | (* (* (not (in ":\n"))) ": " | ||
| 756 | (* (not (in "\r\n"))) "\r\n") | ||
| 757 | "\r\n") | ||
| 750 | (+ (point) 100) | 758 | (+ (point) 100) |
| 751 | t) | 759 | t) |
| 752 | (string-to-number (match-string 1)))) | 760 | (string-to-number (match-string 1)))) |